מסמך עזר בנושא תחביר של Common Expression Language ל-Data Connect

במדריך הזה מוסבר על התחביר של Common Expression Language ‏ (CEL) שרלוונטי ליצירת ביטויים להוראות @auth(expr:) ו-@check(expr:).

מידע מלא על CEL זמין במפרט של CEL.

בדיקת משתנים שמועברים בשאילתות ובמוטציות

התחביר של @auth(expr) מאפשר לכם לגשת למשתנים משאילתות ומוטציות ולבדוק אותם.

לדוגמה, אפשר לכלול משתנה של פעולה, כמו $status, באמצעות vars.status.

mutation Update($id: UUID!, $status: Any) @auth(expr: "has(vars.status)")

נתונים שזמינים לביטויים: בקשה, תגובה, this

אתם משתמשים בנתונים כדי:

  • הערכה באמצעות ביטויי CEL בהנחיות @auth(expr:) ו-@check(expr:)
  • הקצאה באמצעות ביטויי שרת, <field>_expr.

שני ביטויי ה-CEL‏, @auth(expr:) ו-@check(expr:), יכולים להעריך את הדברים הבאים:

  • request.operationName
  • vars (כתובת אימייל חלופית של request.variables)
  • auth (כתובת אימייל חלופית של request.auth)

במוטציות, אפשר לגשת לתוכן של:

  • response (כדי לבדוק תוצאות חלקיות בלוגיקה מרובת שלבים)

בנוסף, ביטויים של @check(expr:) יכולים להעריך:

  • this (הערך של השדה הנוכחי)
  • response (כדי לבדוק תוצאות חלקיות בלוגיקה מרובת שלבים)

הקישור request.operationName

הקישור request.operarationName מאחסן את סוג הפעולה, שאלה או שינוי.

הקישור vars (request.vars)

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

אפשר להשתמש ב-vars.<variablename> בביטוי בתור כינוי ל-request.variables.<variablename> המלא:

# The following are equivalent
mutation StringType($v: String!) @auth(expr: "vars.v == 'hello'")
mutation StringType($v: String!) @auth(expr: "request.variables.v == 'hello'")

הקישור auth (request.auth)

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

במסננים ובביטויים, אפשר להשתמש ב-auth ככינוי ל-request.auth.

הקישור של ההרשאה מכיל את הפרטים הבאים:

  • uid: מזהה משתמש ייחודי שהוקצה למשתמש ששלח את הבקשה.
  • token: מפה של ערכים שנאספו על ידי Authentication.

לפרטים נוספים על התוכן של auth.token אפשר לעיין במאמר נתונים באסימוני אימות

הקישור response

הקישור response מכיל את הנתונים שהשרת מרכיב בתגובה לשאילתה או לשינוי בזמן שהנתונים האלה מורכבים.

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

הקישור response בנוי בהתאם לצורה של הפעולה המשויכת, כולל שדות מוטמעים (מרובים) ושאילתות מוטמעות (אם רלוונטי).

שימו לב: כשניגשים לנתוני תגובה של שאילתות מוטמעות, השדות יכולים להכיל כל סוג נתונים, בהתאם לנתונים שהתבקשו בשאילתה המוטמעת. כשניגשים לנתונים שמוחזרים על ידי שדות מוטציות כמו _inserts ו-_deletes, הם עשויים להכיל מפתחות UUID, מספר מחיקות, ערכי null (ראו את ההפניה למוטציות).

לדוגמה:

  • במוטציה שמכילה שאילתה מוטמעת, הכריכה response מכילה נתוני חיפוש ב-response.query.<fieldName>.<fieldName>...., ובמקרה הזה, ב-response.query.todoList וב-response.query.todoList.priority.
mutation CheckTodoPriority(
  $uniqueListName: String!
) {
  # This query is identified as `response.query`
  query @check(expr: "response.query.todoList.priority == 'high'", message: "This list is not for high priority items!") {
    # This field is identified as `response.query.todoList`
    todoList(where: { name: $uniqueListName }) {
      # This field is identified as `response.query.todoList.priority`
      priority
    }
  }
}
  • במוטציה מרובת שלבים, למשל עם כמה שדות _insert, הכריכה response מכילה נתונים חלקיים ב-response.<fieldName>.<fieldName>...., ובמקרה הזה, response.todoList_insert.id.
mutation CreateTodoListWithFirstItem(
  $listName: String!,
  $itemContent: String!
) @transaction {
  # Step 1
  todoList_insert(data: {
    id_expr: "uuidV4()",
    name: $listName,
  })
  # Step 2:
  todo_insert(data: {
    listId_expr: "response.todoList_insert.id" # <-- Grab the newly generated ID from the partial response so far.
    content: $itemContent,
  })
}

הקישור this

הקישור this מוערך לשדה שאליו מצורף ה-directive‏ @check. במקרה בסיסי, יכול להיות שתעריכו תוצאות של שאילתות עם ערך יחיד.

mutation UpdateMovieTitle (
  $movieId: UUID!,
  $newTitle: String!)
  @auth(level: USER)
  @transaction {
  # Step 1: Query and check
  query @redact {
    moviePermission( # Look up a join table called MoviePermission with a compound key.
      key: {movieId: $movieId, userId_expr: "auth.uid"}
    ) {
      # Check if the user has the editor role for the movie. `this` is the string value of `role`.
      # If the parent moviePermission is null, the @check will also fail automatically.
      role @check(expr: "this == 'editor'", message: "You must be an editor of this movie to update title")
    }
  }
  # Step 2: Act
  movie_update(id: $movieId, data: {
    title: $newTitle
  })
}

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

בכל נתיב נתון, אם רכיב אב הוא null או [], לא תהיה גישה לשדה והערכת ה-CEL תדלג על הנתיב הזה. במילים אחרות, ההערכה מתבצעת רק אם this הוא null או לא null, אבל אף פעם לא undefined.

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

mutation UpdateMovieTitle2($movieId: UUID!, $newTitle: String!) @auth(level: USER) @transaction {
  # Step 1: Query and check
  query {
    moviePermissions( # Now we query for a list of all matching MoviePermissions.
      where: {movieId: {eq: $movieId}, userId: {eq_expr: "auth.uid"}}
    # This time we execute the @check on the list, so `this` is the list of objects.
    # We can use the `.exists` macro to check if there is at least one matching entry.
    ) @check(expr: "this.exists(p, p.role == 'editor')", message: "You must be an editor of this movie to update title") {
      role
    }
  }
  # Step 2: Act
  movie_update(id: $movieId, data: {
    title: $newTitle
  })
}

תחביר של ביטויים מורכבים

אפשר לכתוב ביטויים מורכבים יותר על ידי שילוב עם האופרטורים && ו-||.

mutation UpsertUser($username: String!) @auth(expr: "(auth != null) && (vars.username == 'joe')")

בקטע הבא מתוארים כל האופרטורים הזמינים.

אופרטורים וקדימות אופרטורים

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

בהינתן ביטויים שרירותיים a ו-b, שדה f ואינדקס i.

אופרטור תיאור אסוציאטיביות
a[i] a() a.f אינדקס, גישה לשיחות ולשדות משמאל לימין
!a -a שלילה אונרית מימין לשמאל
a/b a%b a*b אופרטורים של כפל משמאל לימין
a+b a-b אופרטורים של חיבור משמאל לימין
a>b a>=b a<b a<=b אופרטורים יחסיים משמאל לימין
a in b האם המקום מופיע ברשימה או במפה משמאל לימין
type(a) == t השוואת סוגים, כאשר t יכול להיות bool,‏ int,‏ float,‏ number,‏ string,‏ list,‏ map,‏ timestamp או duration משמאל לימין
a==b a!=b אופרטורים להשוואה משמאל לימין
a && b AND מותנה משמאל לימין
a || b OR מותנה משמאל לימין
a ? true_value : false_value ביטוי טרנרי משמאל לימין

נתונים בטוקנים של אימות

האובייקט auth.token יכול להכיל את הערכים הבאים:

שדה תיאור
email כתובת האימייל שמשויכת לחשבון, אם יש כזו.
email_verified true אם המשתמש אימת שיש לו גישה לכתובת email. חלק מהספקים מאמתים אוטומטית כתובות אימייל שנמצאות בבעלותם.
phone_number מספר הטלפון שמשויך לחשבון, אם יש כזה.
name השם המוצג של המשתמש, אם הוא מוגדר.
sub מזהה המשתמש ב-Firebase. הערך הזה ייחודי במסגרת פרויקט.
firebase.identities מילון של כל הזהויות שמשויכות לחשבון של המשתמש הזה. המפתחות של המילון יכולים להיות כל אחד מהערכים הבאים: email, ‏phone, ‏google.com, ‏facebook.com, ‏github.com, ‏twitter.com. הערכים במילון הם מערכים של מזהים ייחודיים לכל ספק זהויות שמשויך לחשבון. לדוגמה, auth.token.firebase.identities["google.com"][0] מכיל את מזהה המשתמש הראשון ב-Google שמשויך לחשבון.
firebase.sign_in_provider ספק הכניסה ששימש לקבלת האסימון הזה. יכול להיות אחת מהמחרוזות הבאות: custom, ‏ password, ‏ phone, ‏ anonymous, ‏ google.com, ‏ facebook.com, ‏ github.com, ‏ twitter.com.
firebase.tenant מזהה הדייר שמשויך לחשבון, אם קיים. לדוגמה, tenant2-m6tyz

שדות נוספים באסימוני JWT מזהים

אפשר גם לגשת לשדות הבאים של auth.token:

הצהרות טוקן בהתאמה אישית
alg אלגוריתם "RS256"
iss הונפק על ידי: כתובת האימייל של חשבון השירות בפרויקט
sub נושא כתובת האימייל של חשבון השירות בפרויקט
aud קהל "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat הזמן שבו הונפק האסימון הזמן הנוכחי, בשניות מאז ראשית זמן יוניקס (Unix epoch)
exp מועד תפוגה הזמן שבו יפוג תוקף האסימון, בשניות מאז ראשית זמן יוניקס (Unix epoch). התאריך יכול להיות עד 3,600 שניות אחרי התאריך iat.
הערה: ההגדרה הזו קובעת רק את הזמן שבו יפוג התוקף של האסימון המותאם אישית עצמו. אבל אחרי שמתחברים לחשבון של משתמש באמצעות signInWithCustomToken(), הוא יישאר מחובר למכשיר עד שהסשן שלו יבוטל או עד שהמשתמש יתנתק.
ֶ<claims> (אופציונלי) טענות אופציונליות בהתאמה אישית שייכללו בטוקן, שאפשר לגשת אליהן דרך auth.token (או request.auth.token) בביטויים. לדוגמה, אם יוצרים טענה מותאמת אישית adminClaim, אפשר לגשת אליה באמצעות auth.token.adminClaim.