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

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

As informações de referência completas 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: request, response, this

Você usa dados para:

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

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

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

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

  • response (para verificar resultados parciais em uma 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 em uma 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 suas expressões acessem todas as variáveis transmitidas na consulta ou mutação.

Você pode 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)

O 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 à medida que esses dados são montados.

À medida que a operação avança e cada etapa é concluída com sucesso, 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 campos aninhados (múltiplos) e consultas incorporadas (se aplicável).

Ao acessar dados de resposta de consulta incorporada, os campos podem conter qualquer tipo de dado, dependendo dos dados solicitados na consulta incorporada. Ao acessar dados retornados por campos de mutação, como _inserts e _deletes, eles podem conter chaves UUID, número de exclusões, 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 de 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 como o campo a que a diretiva @check está anexada. Em um caso básico, você pode avaliar resultados de consultas 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 um ancestral é uma lista, cada ocorrência será testada com this vinculada 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 deles.

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 tenantId associado à conta, se houver. Por exemplo, tenant2-m6tyz.

Campos adicionais em tokens de ID JWT

Você também pode 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) em expressões. Por exemplo, se você criar uma declaração personalizada adminClaim, poderá acessá-la com auth.token.adminClaim.