טריגרים של Data Connect ל-Cloud Functions

באמצעות Cloud Functions for Firebase, אפשר לנהל אירועים בFirebase Data Connect. ‫Cloud Functions מאפשר להריץ קוד מצד השרת בתגובה לאירועים, כמו הפעלה של מוטציה בשירות Data Connect. כך תוכלו להוסיף לוגיקה מותאמת אישית בלי לפרוס שרתים משלכם.

תרחישים נפוצים לדוגמה

  • סנכרון נתונים: שכפול או סנכרון של נתונים עם מערכות אחרות (כמו Cloud Firestore,‏ BigQuery או ממשקי API חיצוניים) אחרי שמתרחש שינוי.

  • תהליכי עבודה אסינכרוניים: אפשר להפעיל תהליכים ארוכים, כמו עיבוד תמונות או צבירת נתונים, אחרי שינוי במסד הנתונים.

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

הפעלת פונקציה ב-Data Connect mutation

אתם יכולים להפעיל פונקציה בכל פעם שמתבצעת Data Connect מוטציה באמצעות onMutationExecuted event handler. הטריגר הזה מופעל כשמבצעים מוטציה.

פונקציית אירוע Mutation בסיסית

הדוגמה הבסיסית הבאה היא פונקציה שמתעדת את הפרטים של כל מוטציה שמופעלת בשירות Data Connect:

Node.js

import { onMutationExecuted } from "firebase-functions/dataconnect";
import { logger } from "firebase-functions";

export const logMutation = onMutationExecuted(
  {
    /* Trigger on all mutations, spanning all services and connectors
       in us-central1 */
  },
  (event) => {
    logger.info("A mutation was executed!", {
      data: event.data,
    });
  }
);

Python

from firebase_functions import dataconnect_fn, logger

@dataconnect_fn.on_mutation_executed()
def log_mutation(event: dataconnect_fn.Event):
  logger.info("A mutation was executed!", event.data)

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

הגדרת המיקום של הפונקציה

מיקום הפונקציה צריך להיות זהה למיקום השירות Data Connect כדי שהאירועים יפעילו את הפונקציה. כברירת מחדל, האזור של הפונקציה הוא us-central1.

Node.js

import { onMutationExecuted } from "firebase-functions/dataconnect";

export const onMutationRegionOption = onMutationExecuted(
  {
    region: "europe-west1"  // Set if Data Connect service location is not us-central1
  },
  (event) => { /* ... */ }
);

Python

@dataconnect_fn.on_mutation_executed(
  region="europe-west1"  # Set if Data Connect service location is not us-central1
)
def mutation_executed_handler_region_option(event: dataconnect_fn.Event):
  pass

סינון אירועים

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

אפשר לסנן לפי service, connector וoperation:

Node.js

import { onMutationExecuted } from "firebase-functions/dataconnect";
import { logger } from "firebase-functions";

// Trigger this function only for the CreateUser mutation
// in the users connector of the myAppService service.
export const onUserCreate = onMutationExecuted(
  {
    service: "myAppService",
    connector: "users",
    operation: "CreateUser",
  },
  (event) => {
    logger.info("A new user was created!", event.data);
    // Add logic here: for example, sending a welcome email.
  }
);

Python

from firebase_functions import dataconnect_fn, logger

@dataconnect_fn.on_mutation_executed(
  service="myAppService",
  connector="users",
  operation="CreateUser"
):
def on_user_create(event: dataconnect_fn.Event):
  logger.info("A new user was created!", event.data)

תווים כלליים לחיפוש וקבוצות ללכידה

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

דוגמאות:

Node.js

import { onMutationExecuted } from "firebase-functions/dataconnect";

// Trigger on all operations that match the pattern `User*`, on any service and
// connector.
export const onMutationWildcards = onMutationExecuted(
  {
    operation: "User*",
  },
  (event) => {}
);

// Trigger on all operations that match the pattern `User*`, on any service and
// connector. Capture the operation name in the variable `op`.
export const onMutationCaptureWildcards = onMutationExecuted(
  {
    operation: "{op=User*}",
  },
  (event) => {
    // `event.params.op` contains the operation name.
  }
);

// Trigger on all operations on the service `myAppService`. Capture the
// operation name in the variable `operation`.
export const onMutationCaptures = onMutationExecuted(
  {
    service: "myAppService",
    operation: "{operation}",
  },
  (event) => {
    // `event.params.operation` contains the operation name.
  }
);

Python

from firebase_functions import dataconnect_fn

# Trigger on all operations that match the pattern `User*`, on any service and
# connector.
@dataconnect_fn.on_mutation_executed(
  operation="User*"
)
def on_mutation_wildcards(event: dataconnect_fn.Event):
  pass

# Trigger on all operations that match the pattern `User*`, on any service and
# connector. Capture the operation name in the variable `op`.
@dataconnect_fn.on_mutation_executed(
  operation="{op=User*}"
)
def on_mutation_capture_wildcards(event: dataconnect_fn.Event):
  # `event.params["op"]` contains the operation name.
  pass

# Trigger on all operations on the service `myAppService`. Capture the
# operation name in the variable `operation`.
@dataconnect_fn.on_mutation_executed(
  service="myAppService",
  operation="{operation}"
)
def on_mutation_captures(event: dataconnect_fn.Event):
  # `event.params["operation"]` contains the operation name.
  pass

גישה לפרטי אימות משתמשים

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

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

Node.js

import { onMutationExecuted } from "firebase-functions/dataconnect";

export const onMutation = onMutationExecuted(
  { operation: "MyMutation" },
  (event) => {
    // mutationExecuted event provides authType and authId:
    // event.authType
    // event.authId
  }
);

Python

from firebase_functions import dataconnect_fn

@dataconnect_fn.on_mutation_executed(operation="MyMutation")
def mutation_executed_handler(event: dataconnect_fn.Event):
  # mutationExecuted event provides auth_type and auth_id, which are accessed as follows
  # event.auth_type
  # event.auth_id
  pass

סוג האימות ומזהה האימות יאוכלסו באופן הבא:

המוטציה הופעלה על ידי authtype authid
משתמש קצה מאומת app_user מזהה משתמש (UID) של טוקן אימות ב-Firebase
משתמש קצה לא מאומת unauthenticated ריק
Admin SDK מתחזה למשתמש קצה app_user מזהה המשתמש (UID) של האסימון של Firebase Auth של המשתמש שהוגדר כמשתמש שמבצע פעולות בשם משתמש אחר
‫Admin SDK מתחזה לבקשה לא מאומתת unauthenticated ריק
Admin SDK עם הרשאות מלאות admin ריק

גישה לנתוני אירועים

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

מאפייני אירוע

מאפיין סוג תיאור
id string מזהה ייחודי של האירוע.
source string משאב המחבר שיצר את האירוע (לדוגמה, //firebasedataconnect.googleapis.com/projects/*/locations/*/services/*/connectors/*).
specversion string גרסת המפרט CloudEvents (למשל, ‫"1.0").
type string סוג האירוע: google.firebase.dataconnect.connector.v1.mutationExecuted.
time string חותמת הזמן (בפורמט ISO 8601) של מועד יצירת האירוע.
subject string זה שינוי אופציונלי. מידע נוסף על ההקשר של האירוע, כמו שם הפעולה.
params object מפה של דפוסי נתיבים שצולמו.
authType string סוג של enum שמייצג את סוג הגורם שהפעיל את האירוע.
authId string מזהה ייחודי של הגורם הראשי שהפעיל את האירוע.
data MutationEventData המטען הייעודי של אירוע Data Connect. מידע נוסף מופיע בקטע הבא.

מטען ייעודי (payload) של נתונים

האובייקט MutationEventData מכיל את המטען הייעודי (Payload) של האירוע Data Connect:

{
  // ...
  "authType": // ...
  "data": {
    "payload": {
      "variables": {
        "userId": "user123",
        "updateData": {
          "displayName": "New Name"
        }
      },
      "data": {
        "updateUser": {
          "id": "user123",
          "displayName": "New Name",
          "email": "user@example.com"
        }
      },
      "errors": []
    }
  }
}
  • payload.variables: אובייקט שמכיל את המשתנים שהועברו למוטציה.
  • payload.data: אובייקט שמכיל את הנתונים שהוחזרו על ידי המוטציה.
  • payload.errors: מערך של שגיאות שהתרחשו במהלך הביצוע של המוטציה. אם השינוי בוצע בהצלחה, המערך הזה יהיה ריק.

דוגמה

כך ניגשים למשתני המוטציה ולנתונים שמוחזרים:

Node.js

import { onMutationExecuted } from "firebase-functions/dataconnect";
import { logger } from "firebase-functions";

export const processNewUserData = onMutationExecuted(
  {
    "service": "myAppService",
    "connector": "users",
    "operation": "CreateUser",
  },
  (event) => {
    // The variables passed to the mutation
    const mutationVariables = event.data.payload.variables;

    // The data returned by the mutation
    const returnedData = event.data.payload.data;

    logger.info("Processing mutation with variables:", mutationVariables);
    logger.info("Mutation returned:", returnedData);

    // ... your custom logic here
  }
);

Python

from firebase_functions import dataconnect_fn, logger

@dataconnect_fn.on_mutation_executed(
  service="myAppService",
  connector="users",
  operation="CreateUser"
):
def process_new_user_data(event: dataconnect_fn.Event):
  # The variables passed to the mutation
  mutation_vars = event.data.payload.variables
  # The data returned by the mutation
  returned_data = event.data.payload.data

  logger.info("Processing mutation with variables:", mutationVariables)
  logger.info("Mutation returned", returnedData)

  # ... your custom logic here

שימו לב שבניגוד לכמה טריגרים אחרים של מסדי נתונים, כמו Cloud Firestore או Realtime Database, האירוע Data Connect לא מספק תמונת מצב של הנתונים לפני השינוי. מכיוון ש-Data Connect מעביר בקשות למסד הנתונים הבסיסי, אי אפשר לקבל את תמונת המצב של הנתונים לפני העיבוד באופן טרנזקציונלי. במקום זאת, יש לכם גישה לארגומנטים שנשלחו למוטציה ולנתונים שהוחזרו ממנה.

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