Riferimento alla sintassi di Common Expression Language per Data Connect

Questa guida di riferimento illustra la sintassi di Common Expression Language (CEL) pertinente alla creazione di espressioni per le direttive @auth(expr:) e @check(expr:).

Le informazioni di riferimento complete per CEL sono fornite nella specifica CEL.

Variabili di test trasmesse in query e mutazioni

La sintassi @auth(expr) ti consente di accedere e testare le variabili di query e mutazioni.

Ad esempio, puoi includere una variabile di operazione, ad esempio $status, utilizzando vars.status.

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

Dati disponibili per le espressioni: request, response, this

Utilizzi i dati per:

  • Valutazione con espressioni CEL nelle direttive @auth(expr:) e @check(expr:)
  • Assegnazione mediante espressioni del server, <field>_expr.

Entrambe le espressioni CEL @auth(expr:) e @check(expr:) possono valutare quanto segue:

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

Nelle mutazioni, puoi accedere e assegnare i contenuti di:

  • response (per controllare i risultati parziali nella logica multi-step)

Inoltre, le espressioni @check(expr:) possono valutare:

  • this (il valore del campo corrente)
  • response (per controllare i risultati parziali nella logica multi-step)

Il binding request.operationName

Il binding request.operarationName memorizza il tipo di operazione, query o mutazione.

Il binding vars (request.vars)

L'associazione vars consente alle espressioni di accedere a tutte le variabili trasmesse nella query o nella mutazione.

Puoi utilizzare vars.<variablename> in un'espressione come alias per request.variables.<variablename> completo:

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

Il binding auth (request.auth)

Authentication identifica gli utenti che richiedono l'accesso ai tuoi dati e fornisce queste informazioni come binding su cui puoi basarti nelle tue espressioni.

Nei filtri e nelle espressioni, puoi utilizzare auth come alias per request.auth.

Il binding di autorizzazione contiene le seguenti informazioni:

  • uid: un ID utente univoco assegnato all'utente richiedente.
  • token: una mappa dei valori raccolti da Authentication.

Per maggiori dettagli sui contenuti di auth.token, vedi Dati nei token di autenticazione

L'associazione response

Il binding response contiene i dati assemblati dal server in risposta a una query o una mutazione man mano che vengono assemblati.

Man mano che l'operazione procede, quando ogni passaggio viene completato correttamente, response contiene i dati di risposta dei passaggi completati correttamente.

Il binding response è strutturato in base alla forma dell'operazione associata, inclusi campi nidificati (multipli) e query incorporate (se applicabile).

Tieni presente che quando accedi ai dati di risposta alle query incorporate, i campi possono contenere qualsiasi tipo di dati, a seconda dei dati richiesti nella query incorporata; quando accedi ai dati restituiti dai campi di mutazione come _insert e _delete, questi possono contenere chiavi UUID, numero di eliminazioni, valori null (vedi il riferimento alle mutazioni).

Ad esempio:

  • In una mutazione che contiene una query incorporata, il binding response contiene i dati di ricerca in response.query.<fieldName>.<fieldName>...., in questo 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
    }
  }
}
  • In una mutazione in più passaggi, ad esempio con più campi _insert, il binding response contiene dati parziali in response.<fieldName>.<fieldName>...., in questo 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,
  })
}

L'associazione this

L'associazione this restituisce il campo a cui è collegata l'istruzione @check. In un caso di base, potresti valutare i risultati di query a valore singolo.

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 il campo restituito si verifica più volte perché un antenato è un elenco, ogni occorrenza viene testata con this associato a ogni valore.

Per un determinato percorso, se un antenato è null o [], il campo non verrà raggiunto e la valutazione CEL verrà ignorata per quel percorso. In altre parole, la valutazione viene eseguita solo quando this è null o non null, ma mai undefined.

Quando il campo stesso è un elenco o un oggetto, this segue la stessa struttura (inclusi tutti i discendenti selezionati in caso di oggetti), come illustrato nell'esempio seguente.

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

Sintassi delle espressioni complesse

Puoi scrivere espressioni più complesse combinandole con gli operatori && e ||.

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

La sezione seguente descrive tutti gli operatori disponibili.

Operatori e precedenza degli operatori

Utilizza la seguente tabella come riferimento per gli operatori e la relativa precedenza.

Date le espressioni arbitrarie a e b, un campo f e un indice i.

Operatore Descrizione Associatività
a[i] a() a.f Accesso a indice, chiamata e campo da sinistra a destra
!a -a Negazione unaria da destra a sinistra
a/b a%b a*b Operatori moltiplicativi da sinistra a destra
a+b a-b Operatori additivi da sinistra a destra
a>b a>=b a<b a<=b Operatori relazionali da sinistra a destra
a in b Esistenza nell'elenco o nella mappa da sinistra a destra
type(a) == t Confronto tra tipi, in cui t può essere bool, int, float, number, string, list, map, timestamp o duration da sinistra a destra
a==b a!=b Operatori di confronto da sinistra a destra
a && b AND condizionale da sinistra a destra
a || b OR condizionale da sinistra a destra
a ? true_value : false_value Espressione ternaria da sinistra a destra

Dati nei token di autorizzazione

L'oggetto auth.token può contenere i seguenti valori:

Campo Descrizione
email L'indirizzo email associato all'account, se presente.
email_verified true se l'utente ha verificato di avere accesso all'indirizzo email. Alcuni provider verificano automaticamente gli indirizzi email di loro proprietà.
phone_number Il numero di telefono associato all'account, se presente.
name Il nome visualizzato dell'utente, se impostato.
sub L'UID Firebase dell'utente. Questo valore è univoco all'interno di un progetto.
firebase.identities Dizionario di tutte le identità associate all'account di questo utente. Le chiavi del dizionario possono essere una delle seguenti: email, phone, google.com, facebook.com, github.com, twitter.com. I valori del dizionario sono array di identificatori univoci per ogni provider di identità associato all'account. Ad esempio, auth.token.firebase.identities["google.com"][0] contiene il primo ID utente Google associato all'account.
firebase.sign_in_provider Il provider di accesso utilizzato per ottenere questo token. Può essere una delle seguenti stringhe: custom, password, phone, anonymous, google.com, facebook.com, github.com, twitter.com.
firebase.tenant L'ID tenant associato all'account, se presente. Ad esempio, tenant2-m6tyz

Campi aggiuntivi nei token ID JWT

Puoi anche accedere ai seguenti campi auth.token:

Attestazioni token personalizzate
alg Algoritmo "RS256"
iss Emittente L'indirizzo email del service account del tuo progetto
sub Oggetto L'indirizzo email del service account del tuo progetto
aud Pubblico "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat Ora di emissione L'ora attuale, in secondi a partire dall'epoca UNIX
exp Scadenza Il momento in cui il token scade, espresso in secondi a partire dal tempo Unix. Può essere al massimo 3600 secondi dopo iat.
Nota: questo valore controlla solo la scadenza del token personalizzato. Tuttavia, una volta che un utente ha eseguito l'accesso utilizzando signInWithCustomToken(), l'accesso rimarrà attivo sul dispositivo finché la sessione non viene invalidata o l'utente non esce.
(Facoltativo) <claims> Rivendicazioni personalizzate facoltative da includere nel token, a cui è possibile accedere tramite auth.token (o request.auth.token) nelle espressioni. Ad esempio, se crei un'attestazione personalizzata adminClaim, puoi accedervi con auth.token.adminClaim.