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

Ten przewodnik zawiera składnię języka Common Expression Language (CEL) związaną z tworzeniem wyrażeń dla dyrektyw @auth(expr:) i @check(expr:).

Pełne informacje referencyjne dotyczące 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 w wyrażeniach: request, response, this

Dane są wykorzystywane do:

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

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

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

W mutacjach możesz uzyskać dostęp do zawartości tych elementów i przypisać ją do nich:

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

Dodatkowo wyrażenia @check(expr:) mogą sprawdzać:

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

Powiązanie request.operationName

Wiązanie request.operarationName przechowuje typ operacji, czyli zapytanie lub zmianę.

Powiązanie vars (request.vars)

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

W wyrażeniu możesz użyć atrybutu vars.<variablename> jako aliasu dla w pełni kwalifikowanego atrybutu 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 budować wyrażenia.

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

Powiązanie autoryzacji 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 uwierzytelniających.

Powiązanie response

response powiązanie zawiera dane, które serwer gromadzi w odpowiedzi na zapytanie lub mutację w trakcie ich gromadzenia.

W miarę postępu operacji, po pomyślnym zakończeniu każdego kroku, pole response zawiera dane odpowiedzi z pomyślnie zakończonych kroków.

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

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, o które prosisz w zapytaniu osadzonym. Gdy uzyskujesz dostęp do danych zwracanych przez pola mutacji, takie jak _inserts i _deletes, mogą one zawierać klucze UUID, liczbę usunięć i wartości null (patrz dokumentacja mutacji).

Przykład:

  • W mutacji zawierającej zapytanie zagnieżdżone powiązanie response zawiera dane wyszukiwania w response.query.<fieldName>.<fieldName>...., w tym przypadku response.query.todoListresponse.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 przypadku mutacji wieloetapowej, na przykład z wieloma polami _insert, powiązanie response zawiera częściowe dane w polu 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 obliczane na podstawie pola, do którego jest dołączona dyrektywa @check. W podstawowym przypadku możesz oceniać wyniki zapytań jednowartościowych.

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 zwrócone pole występuje wiele razy, ponieważ dowolny element nadrzędny jest listą, każde wystąpienie jest testowane z parametrem this powiązanym 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 jest przeprowadzana tylko wtedy, gdy wartość this to null lub wartość niebędąca null, ale nigdy undefined.

Jeśli pole jest listą lub obiektem, this ma taką samą strukturę (w przypadku obiektów obejmuje wszystkie wybrane elementy podrzędne), jak pokazano w przykładzie poniżej.

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 z operatorami &&||.

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

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

Operatory i ich kolejność

W poniższej tabeli znajdziesz operatory i ich priorytety.

Dane są wyrażeniami ab, polem f oraz indeksem i.

Operator Opis Łączność
a[i] a() a.f Indeksowanie, wywoływanie, dostęp do pól 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 Obecność na liście lub mapie od lewej do prawej,
type(a) == t porównanie typów, 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 Warunkowe ORAZ od lewej do prawej,
a || b Warunkowe LUB 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 Firebase użytkownika. 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 być jednym z tych ciągów: custom, password, phone, anonymous, google.com, facebook.com, github.com, twitter.com.
firebase.tenant Identyfikator tenantId powiązany z kontem (jeśli występuje). Na przykład tenant2-m6tyz

Dodatkowe pola w tokenach identyfikatorów JWT

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

Deklaracje tokenów niestandardowych
alg Algorytm "RS256"
iss Wystawca Adres e-mail konta usługi w projekcie
sub Temat Adres e-mail konta usługi w projekcie
aud Odbiorcy "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat Godzina wydania Bieżący czas w sekundach od początku epoki systemu UNIX.
exp Okres ważności Czas wygaśnięcia tokena podany w sekundach od początku epoki systemu UNIX. Może być maksymalnie o 3600 sekund późniejsza niż wartość iat.
Uwaga: to ustawienie kontroluje tylko czas wygaśnięcia samego tokena niestandardowego. Gdy jednak zalogujesz użytkownika za pomocą signInWithCustomToken(), pozostanie on zalogowany na urządzeniu, dopóki jego sesja nie straci ważności lub użytkownik się nie wyloguje.
<claims> (opcjonalnie) Opcjonalne roszczenia niestandardowe do uwzględnienia w tokenie, do których można uzyskać dostęp za pomocą symbolu auth.token (lub request.auth.token) w wyrażeniach. Jeśli na przykład utworzysz roszczenie niestandardowe adminClaim, możesz uzyskać do niego dostęp za pomocą auth.token.adminClaim.