Referência da sintaxe da Common Expression Language para o SQL Connect

Este guia de referência aborda a sintaxe da Common Expression Language (CEL) relevante para a criação de expressões para as diretivas @auth(expr:), @check(expr:) e @refresh(onMutationExecuted:).

Informações de referência completas para CEL são fornecidas na especificação da CEL.

Variáveis de teste 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:), @check(expr:), @refresh(onMutationExecuted:) podem avaliar o seguinte:

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

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

E as expressões @refresh(onMutationExecuted:) têm a seguinte vinculação:

  • mutation.variables
  • mutation.auth

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.

É possível usar vars.<variablename> em uma expressão como um alias para o totalmente qualificado 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'")

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 criada 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 pelo 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 de etapas concluídas com sucesso.

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

Observe que, ao acessar dados de resposta de consulta incorporada, os campos podem conter qualquer tipo de dados, 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 response vinculação contém dados de pesquisa em response.query.<fieldName>.<fieldName>...., em 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 response vinculação contém dados parciais em response.<fieldName>.<fieldName>...., nesse 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 no campo ao qual a diretiva @check está anexada. Em um caso básico, é possível 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 qualquer 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á acessado e a avaliação da CEL será ignorada para esse caminho. Em outras palavras, a avaliação só ocorre quando this é null ou não null, mas nunca undefined.

Quando o campo em si é 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 && e || operadores.

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