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) przydatnej do tworzenia wyrażeń w dyrektywach @auth(expr:) i @check(expr:).

Pełne informacje referencyjne dotyczące języka 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 użyć zmiennej operacji, takiej jak $status, za pomocą funkcji 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:

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

Zarówno wyrażenia @auth(expr:), jak i @check(expr:) w języku CEL mogą oceniać:

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

W mutacjach możesz uzyskać dostęp do treści i przypisać je do:

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

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

  • 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 przekazanych w zapytaniu lub mutacji.

Możesz użyć atrybutu vars.<variablename> w wyrażeniu jako aliasu pełnego 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 przekazuje te informacje jako ograniczenie, na którym możesz oprzeć swoje wyrażenia.

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

Powiązanie autoryzacji zawiera te informacje:

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

Więcej informacji o treściach w polu auth.token znajdziesz w artykule Dane w tokenach uwierzytelniania.

Powiązanie response

Wiązanie response zawiera dane, które są tworzone przez serwer w odpowiedzi na zapytanie lub mutację w miarę ich tworzenia.

W miarę postępu operacji, po zakończeniu każdego kroku,response zawiera dane odpowiedzi z każdego kroku, który został wykonany pomyślnie.

Wiązanie response jest ustrukturyzowane zgodnie z formą powiązanej operacji, w tym (wielokrotnych) zagnieżdżonych pól i (w stosownych przypadkach) osadzonych zapytań.

Pamiętaj, że gdy uzyskujesz dostęp do danych odpowiedzi w ramach osadzonej kwerendy, pola mogą zawierać dowolny typ danych, w zależności od danych zażądanych w ramach osadzonej kwerendy. Gdy uzyskujesz dostęp do danych zwróconych przez pola mutacji, takie jak _insert_delete, mogą one zawierać klucze UUID, liczbę usunięcia i wartości null (patrz dokumentacja mutacji).

Przykład:

  • W zmianie, która zawiera osadzone zapytanie, parametr response zawiera dane wyszukiwania w miejscu 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, element 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 zwraca pole, do którego jest dołączona dyrektywa @check. W najprostszym przypadku możesz analizować wyniki zapytania o pojedynczej 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 kilka razy, ponieważ każdy z jego przodków jest listą, każde wystąpienie jest testowane z użyciem this powiązanego z każdą wartością.

Jeśli w przypadku dowolnej ścieżki dowolny z jej przodków to null lub [], to nie dojdzie do odwołania się do pola, a ocena CEL zostanie pominięta. Inaczej mówiąc, sprawdzanie odbywa się tylko wtedy, gdy this ma wartość null lub nie-null, ale nigdy undefined.

Gdy samo pole jest listą lub obiektem, this ma taką samą strukturę (w tym wszystkich potomków wybranych w przypadku obiektów), jak 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ń

Aby tworzyć bardziej złożone wyrażenia, możesz łączyć operatory &&||.

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

W sekcji poniżej opisujemy wszystkie dostępne operatory.

Operatory i pierwszeństwo operatorów

Poniższa tabela zawiera informacje o operatorach i odpowiednim dla nich priorytecie.

Załóżmy dowolne wyrażenia ab, pole f oraz indeks i.

Operator Opis Związek
a[i] a() a.f Indeksowanie, wywoływanie i dostęp do pól od lewej do prawej.
!a -a Negacja unarna od prawej do lewej
a/b a%b a*b Operatory mnożenia od lewej do prawej.
a+b a-b Operatory addytywne od lewej do prawej.
a>b a>=b a<b a<=b Operatory relacji od lewej do prawej.
a in b występowanie na liście lub 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 Warunkowe ORAZ od lewej do prawej.
a || b Warunkowe LUB od lewej do prawej.
a ? true_value : false_value Wyrażenie warunkowe 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 istnieje).
email_verified true, jeśli użytkownik potwierdził, że ma dostęp do adresu email. Niektórzy dostawcy automatycznie weryfikują należące do nich adresy e-mail.
phone_number numer telefonu powiązany z kontem (jeśli istnieje);
name Wyświetlana nazwa użytkownika (jeśli została ustawiona).
sub Identyfikator Firebase użytkownika. Musi być unikalny w ramach projektu.
firebase.identities Słownik wszystkich tożsamości powiązanych z kontem tego użytkownika. Klucze słownika mogą być dowolnymi z tych wartości: email, phone, google.com, facebook.com, github.com, twitter.com. Wartości w słowniku to tablice unikalnych identyfikatorów dla każdego dostawcy tożsamości powiązanego z kontem. Na przykład auth.token.firebase.identities["google.com"][0] zawiera pierwsze identyfikator użytkownika Google powiązany z kontem.
firebase.sign_in_provider Dostawca logowania użyty do uzyskania tego tokena. Może być dowolnym 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 identyfikatorach 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 projektu;
sub Temat adres e-mail konta usługi projektu;
aud Odbiorcy "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat Issued-at time bieżący czas w sekundach od początku epoki UNIX
exp Okres ważności Czas w sekundach od początku epoki UNIX, w którym token traci ważność. Może ona nastąpić maksymalnie 3600 sekund później niż iat.
Uwaga: to ustawienie określa tylko czas wygaśnięcia tokenu niestandardowego. Jednak po zalogowaniu użytkownika za pomocą opcji signInWithCustomToken() będzie on zalogowany na urządzeniu do czasu unieważnienia sesji lub wylogowania.
<claims> (opcjonalnie) Opcjonalne oświadczenia niestandardowe do uwzględnienia w tokenie, do których można uzyskać dostęp za pomocą wyrażenia auth.token (lub request.auth.token). Jeśli na przykład utworzysz roszczenie niestandardowe adminClaim, możesz uzyskać do niego dostęp za pomocą auth.token.adminClaim.