Referência da sintaxe da linguagem de expressão comum para o Data Connect

Este guia de referência aborda a sintaxe da linguagem de expressão comum (CEL) relevante para criar expressões para as diretivas @auth(expr:) e @check(expr:).

As informações completas de referência da CEL estão disponíveis na especificação da CEL.

Testar variáveis transmitidas em consultas e mutações

A sintaxe @auth(expr) permite acessar e testar variáveis de consultas e mutações.

Por exemplo, é possível incluir uma variável de operação, como $status, usando vars.status.

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

Dados disponíveis para expressões

As expressões CEL @auth(expr:) e @check(expr:) podem avaliar o seguinte:

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

Além disso, as expressões @check(expr:) podem avaliar:

  • this (o valor do campo atual)

O objeto request.operationName

O objeto request.operarationName armazena o tipo de operação, consulta ou mutação.

O objeto vars

O objeto vars permite que as expressões acessem todas as variáveis transmitidas na consulta ou mutação.

É possível usar vars.<variablename> em uma expressão como um alias para o request.variables.<variablename> totalmente qualificado:

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

O objeto auth

Authentication identifica os usuários que solicitam acesso aos seus dados e fornece essas informações como um objeto que pode ser usado nas suas expressões.

Nos filtros e expressões, é possível usar auth como um alias para request.auth.

O objeto de autenticação contém as seguintes informações:

  • uid: um ID de usuário exclusivo, atribuído ao usuário solicitante.
  • token: um mapa de valores coletados por Authentication.

Para mais detalhes sobre o conteúdo de auth.token, consulte Dados em tokens de autenticação.

A vinculação this

A vinculação this é avaliada como o campo ao qual a diretiva @check está anexada. Em um caso básico, você pode avaliar resultados de consulta de valor único.

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

Se o campo retornado ocorrer várias vezes porque algum ancestral é uma lista, cada ocorrência será testada com this vinculado a cada valor.

Para qualquer caminho, se um ancestral for null ou [], o campo não será alcançado e a avaliação do CEL será ignorada para esse caminho. Em outras palavras, a avaliação só acontece quando this é null ou não é null, mas nunca undefined.

Quando o campo é uma lista ou um objeto, this segue a mesma estrutura (incluindo todos os descendentes selecionados no caso de objetos), conforme ilustrado no exemplo a seguir.

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

Sintaxe de expressão complexa

É possível escrever expressões mais complexas combinando com os operadores && e ||.

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

A seção a seguir descreve todos os operadores disponíveis.

Operadores e precedência do operador

Use a tabela a seguir como referência para os operadores e a precedência correspondente.

Expressões arbitrárias fornecidas a e b, um campo f e um índice i.

Operador Descrição Associatividade
a[i] a() a.f Índice, chamada, acesso ao campo da esquerda para a direita
!a -a Negação unária da direita para a esquerda
a/b a%b a*b Operadores multiplicativos da esquerda para a direita
a+b a-b Operadores aditivos da esquerda para a direita
a>b a>=b a<b a<=b Operadores relacionais da esquerda para a direita
a in b Existência na lista ou no mapa da esquerda para a direita
type(a) == t Comparação de tipos, em que t pode ser bool, int, float, number, string, list, map, timestamp ou duration da esquerda para a direita
a==b a!=b Operadores de comparação da esquerda para a direita
a && b Condicional E da esquerda para a direita
a || b Condicional OU da esquerda para a direita
a ? true_value : false_value Expressão ternária da esquerda para a direita

Dados em tokens de autenticação

O objeto auth.token pode conter os seguintes valores:

Campo Descrição
email O endereço de e-mail associado à conta, se essa informação existir.
email_verified true se o usuário tiver verificado que tem acesso ao endereço email. Alguns provedores verificam automaticamente esses endereços de e-mail.
phone_number O número de telefone associado à conta, se essa informação existir.
name O nome de exibição do usuário, se ele tiver sido definido.
sub O UID do Firebase do usuário. Ele é exclusivo dentro de um projeto.
firebase.identities O dicionário de todas as identidades associadas à conta desse usuário. As chaves do dicionário podem ser qualquer uma das seguintes: email, phone, google.com, facebook.com, github.com, twitter.com. Os valores do dicionário são matrizes de identificadores exclusivos de cada provedor de identidade associado à conta. Por exemplo, auth.token.firebase.identities["google.com"][0] contém o primeiro ID de usuário do Google associado à conta.
firebase.sign_in_provider O provedor de entrada usado para receber esse token. Pode ser uma das seguintes strings: custom, password, phone, anonymous, google.com, facebook.com, github.com, twitter.com.
firebase.tenant O ID do locatário associado à conta, se houver. Por exemplo, tenant2-m6tyz.

Outros campos em tokens de ID JWT

Também é possível acessar os seguintes campos auth.token:

Declarações de tokens personalizados
alg Algoritmo "RS256"
iss Emissor Endereço de e-mail da conta de serviço do seu projeto
sub Assunto Endereço de e-mail da conta de serviço do seu projeto
aud Público "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat Hora de emissão A hora atual, em segundos, desde a época do UNIX
exp Tempo de expiração O tempo, em segundos, desde a época do UNIX, em que o token expira. Pode ser no máximo 3.600 segundos depois de iat.
Observação: ele controla o tempo apenas quando o token personalizado expira. No entanto, quando você faz o login de um usuário utilizando signInWithCustomToken(), ele permanece conectado ao dispositivo até que a sessão seja invalidada ou que o usuário se desconecte.
<claims> (opcional) Declarações personalizadas opcionais a serem incluídas no token, que podem ser acessadas por auth.token (ou request.auth.token) nas expressões. Por exemplo, se você criar uma reivindicação personalizada adminClaim, poderá acessá-la com auth.token.adminClaim.