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

Este guia de referência abrange 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 de referência completas sobre a 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: request, response, this

Você usa dados para:

  • Avaliação com expressões CEL em diretivas @auth(expr:) e @check(expr:)
  • Atribuição usando expressões do servidor, <field>_expr.

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)

Nas mutações, é possível acessar e atribuir o conteúdo de:

  • response (para verificar resultados parciais na lógica de várias etapas)

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

  • this (o valor do campo atual)
  • response (para verificar resultados parciais na lógica de várias etapas)

A vinculação request.operationName

A vinculação request.operarationName armazena o tipo de operação, consulta ou mutação.

A vinculação vars (request.vars)

A vinculação 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'")

A vinculação auth (request.auth)

Authentication identifica os usuários que solicitam acesso aos seus dados e fornece essas informações como uma vinculação que pode ser usada nas suas expressões.

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

A vinculação 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 response

A vinculação response contém os dados que estão sendo montados pelo servidor em resposta a uma consulta ou mutação enquanto esses dados estão sendo montados.

À medida que a operação avança e cada etapa é concluída, response contém dados de resposta das etapas concluídas.

A vinculação response é estruturada de acordo com a forma da operação associada, incluindo (vários) campos aninhados e (se aplicável) consultas embutidas.

Quando você acessa dados de resposta de consulta incorporados, os campos podem conter qualquer tipo de dados, dependendo dos dados solicitados na consulta incorporada. Quando você acessa dados retornados por campos de mutação, como _inserts e _deletes, eles podem conter chaves UUID, número de exclusões e valores nulos (consulte a referência de mutações).

Exemplo:

  • Em uma mutação que contém uma consulta incorporada, a vinculação response contém dados de pesquisa em response.query.<fieldName>.<fieldName>...., neste caso, response.query.todoList e 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
    }
  }
}
  • Em uma mutação em várias etapas, por exemplo, com vários campos _insert, a vinculação response contém dados parciais em response.<fieldName>.<fieldName>...., neste caso, 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,
  })
}

A vinculação this

A vinculação this é avaliada para 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 abaixo.

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.