Dokumentacja składni języka Common Expression Language w usłudze Data Connect

Ten przewodnik zawiera informacje o składni języka Common Expression Language (CEL) przydatne podczas tworzenia wyrażeń dla dyrektyw @auth(expr:) i @check(expr:).

Pełne informacje o języku CEL znajdziesz w specyfikacji CEL.

Testowanie zmiennych przekazywanych w zapytaniach i mutacjach

Składnia @auth(expr) umożliwia dostęp do zmiennych z zapytań i mutacji oraz ich testowanie.

Możesz na przykład uwzględnić zmienną operacji, np. $status, używając vars.status.

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

Dane dostępne dla wyrażeń: request, response, this

Dane są używane do:

  • oceny za pomocą wyrażeń CEL w dyrektywach @auth(expr:) i @check(expr:);
  • przypisywania za pomocą wyrażeń serwera, <field>_expr.

Wyrażenia CEL @auth(expr:) i @check(expr:) mogą oceniać te elementy:

  • request.operationName
  • vars (alias request.variables)
  • auth (alias request.auth)

W mutacjach możesz uzyskiwać dostęp do treści tych elementów i przypisywać je:

  • response (aby sprawdzić wyniki częściowe w logice wieloetapowej).

Dodatkowo wyrażenia @check(expr:) mogą oceniać te elementy:

  • this (wartość bieżącego pola);
  • response (aby sprawdzić wyniki częściowe w logice wieloetapowej).

Powiązanie request.operationName

Powiązanie request.operarationName przechowuje typ operacji, czyli zapytanie lub mutację.

Powiązanie vars (request.vars)

Powiązanie vars umożliwia wyrażeniom dostęp do wszystkich zmiennych przekazywanych w zapytaniu lub mutacji.

W wyrażeniu możesz użyć vars.<variablename> jako aliasu dla pełnej nazwy 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'")

Powiązanie auth (request.auth)

Authentication identyfikuje użytkowników, którzy proszą o dostęp do Twoich danych, i udostępnia te informacje jako powiązanie, na którym możesz opierać się w swoich wyrażeniach.

W filtrach i wyrażeniach możesz używać auth jako aliasu dla request.auth.

Powiązanie auth zawiera te informacje:

  • uid: unikalny identyfikator użytkownika przypisany do użytkownika wysyłającego żądanie.
  • token: mapa wartości zebranych przez Authentication.

Więcej informacji o zawartości auth.token znajdziesz w sekcji Dane w tokenach uwierzytelniania

Powiązanie response

Powiązanie response zawiera dane gromadzone przez serwer w odpowiedzi na zapytanie lub mutację w trakcie ich gromadzenia.

W miarę postępu operacji i pomyślnego ukończenia każdego kroku powiązanie response zawiera dane odpowiedzi z pomyślnie ukończonych kroków.

Powiązanie response jest ustrukturyzowane zgodnie z kształtem powiązanej operacji, w tym (wieloma) zagnieżdżonymi polami i (w stosownych przypadkach) zapytaniami osadzonymi.

Pamiętaj, że gdy uzyskujesz dostęp do danych odpowiedzi na zapytanie osadzone, pola mogą zawierać dowolny typ danych w zależności od danych żądanych w zapytaniu osadzonym. Gdy uzyskujesz dostęp do danych zwracanych przez pola mutacji, takie jak _insert i _delete, mogą one zawierać klucze UUID, liczbę usunięć, wartości null (patrz dokumentacja mutacji).

Przykład:

  • W mutacji, która zawiera zapytanie osadzone, response powiązanie zawiera dane wyszukiwania w response.query.<fieldName>.<fieldName>...., w tym przypadku response.query.todoList i 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
    }
  }
}
  • W mutacji wieloetapowej, na przykład z wieloma _insert polami, powiązanie response zawiera dane częściowe w response.<fieldName>.<fieldName>...., w tym przypadku, 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,
  })
}

Powiązanie this

Powiązanie this jest oceniane jako pole, do którego jest dołączona dyrektywa @check. W podstawowym przypadku możesz oceniać wyniki zapytań z jedną wartością.

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
  })
}

Jeśli zwracane pole występuje wiele razy, ponieważ dowolny element nadrzędny jest listą, każde wystąpienie jest testowane za pomocą powiązania this z każdą wartością.

Jeśli w przypadku danej ścieżki element nadrzędny ma wartość null lub [], pole nie zostanie osiągnięte, a ocena CEL zostanie pominięta w przypadku tej ścieżki. Innymi słowy, ocena odbywa się tylko wtedy, gdy this ma wartość null lub inną niż null, ale nigdy undefined.

Gdy pole jest listą lub obiektem, this ma taką samą strukturę (w tym wszystkie elementy podrzędne wybrane w przypadku obiektów), jak pokazano w tym przykładzie.

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
  })
}

Składnia złożonych wyrażeń

Możesz pisać bardziej złożone wyrażenia, łącząc je za pomocą && i || operatorów.

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

W sekcji poniżej znajdziesz opis wszystkich dostępnych operatorów.

Operatory i kolejność wykonywania operatorów

W tej tabeli znajdziesz informacje o operatorach i ich kolejności wykonywania.

Dane są dowolnymi wyrażeniami a i b, polem f oraz indeksem i.

Operator Opis Łączność
a[i] a() a.f Indeks, wywołanie, dostęp do pola od lewej do prawej
!a -a Negacja jednoargumentowa od prawej do lewej
a/b a%b a*b Operatory mnożenia od lewej do prawej
a+b a-b Operatory dodawania od lewej do prawej
a>b a>=b a<b a<=b Operatory relacji od lewej do prawej
a in b Istnienie na liście lub w mapie od lewej do prawej
type(a) == t Porównanie typu, gdzie t może być wartością logiczną, liczbą całkowitą, liczbą zmiennoprzecinkową, liczbą, ciągiem znaków, listą, mapą, sygnaturą czasową lub czasem trwania od lewej do prawej
a==b a!=b Operatory porównania od lewej do prawej
a && b Warunkowy operator AND od lewej do prawej
a || b Warunkowy operator OR od lewej do prawej
a ? true_value : false_value Wyrażenie trójargumentowe od lewej do prawej

Dane w tokenach uwierzytelniania

Obiekt auth.token może zawierać te wartości:

Pole Opis
email Adres e-mail powiązany z kontem, jeśli jest dostępny.
email_verified true, jeśli użytkownik potwierdził, że ma dostęp do adresu email. Niektórzy dostawcy automatycznie weryfikują adresy e-mail, których są właścicielami.
phone_number Numer telefonu powiązany z kontem, jeśli jest dostępny.
name Wyświetlana nazwa użytkownika, jeśli została ustawiona.
sub Identyfikator UID użytkownika w Firebase. Jest on unikalny w obrębie projektu.
firebase.identities Słownik wszystkich tożsamości powiązanych z kontem tego użytkownika. Klucze słownika mogą być dowolne z tych wartości: email, phone, google.com, facebook.com, github.com, twitter.com. Wartości słownika to tablice unikalnych identyfikatorów każdego dostawcy tożsamości powiązanego z kontem. Na przykład auth.token.firebase.identities["google.com"][0] zawiera pierwszy identyfikator użytkownika Google powiązany z kontem.
firebase.sign_in_provider Dostawca logowania użyty do uzyskania tego tokena. Może to być jeden z tych ciągów znaków: custom, password, phone, anonymous, google.com, facebook.com, github.com, twitter.com.
firebase.tenant Identyfikator tenantId powiązany z kontem, jeśli jest dostępny. Na przykład tenant2-m6tyz.

Dodatkowe pola w tokenach tożsamości JWT

Możesz też uzyskać dostęp do tych pól auth.token:

Niestandardowe deklaracje tokena
alg Algorytm "RS256"
iss Wystawca Adres e-mail konta usługi projektu
sub Temat Adres e-mail konta usługi projektu
aud Odbiorcy "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat Godzina wystawienia Aktualny czas w sekundach od początku epoki UNIX
exp Data ważności Czas w sekundach od początku epoki UNIX, w którym token wygasa. Może to być maksymalnie 3600 sekund później niż iat.
Uwaga: ta wartość określa tylko czas wygaśnięcia tokena niestandardowego samego tokena. Gdy jednak zalogujesz użytkownika za pomocą signInWithCustomToken(), pozostanie on zalogowany na urządzeniu, dopóki jego sesja nie zostanie unieważniona lub użytkownik się nie wyloguje.
<claims> (opcjonalnie) Opcjonalne deklaracje niestandardowe, które mają być uwzględnione w tokenie. Można uzyskać do nich dostęp za pomocą auth.token (lub request.auth.token) w wyrażeniach. Jeśli na przykład utworzysz deklarację niestandardową adminClaim, możesz uzyskać do niej dostęp za pomocą auth.token.adminClaim.