Data Connect 的通用表达式语言语法参考文档

本参考指南介绍了与为 @auth(expr:)@check(expr:) 指令创建表达式相关的通用表达式语言 (CEL) 语法。

如需查看有关 CEL 的完整参考信息,请参阅 CEL 规范

在查询和更改中传入的测试变量

借助 @auth(expr) 语法,您可以访问和测试查询和更改中的变量。

例如,您可以使用 vars.status 添加操作变量(例如 $status)。

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

可供表达式使用的以下数据:request、response、this

您使用数据的用途包括:

  • @auth(expr:)@check(expr:) 指令中使用 CEL 表达式进行评估
  • 使用服务器表达式 <field>_expr 进行赋值。

@auth(expr:)@check(expr:) CEL 表达式都可以评估以下内容:

  • request.operationName
  • varsrequest.variables 的别名)
  • authrequest.auth 的别名)

在更改中,您可以访问和分配以下内容:

  • response(用于检查多步逻辑中的部分结果)

此外,@check(expr:) 表达式还可以评估:

  • this(当前字段的值)
  • response(用于检查多步逻辑中的部分结果)

request.operationName 绑定

request.operarationName 绑定会存储操作类型(查询或更改)。

vars 绑定 (request.vars)

借助 vars 绑定,您的表达式可以访问在查询或更改中传递的所有变量。

您可以在表达式中使用 vars.<variablename> 作为完全限定的 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'")

auth 绑定 (request.auth)

Authentication 可识别请求访问您数据的用户,并以绑定的形式(您可在表达式中构建)提供信息。

在过滤条件和表达式中,您可以将 auth 用作 request.auth 的别名。

身份验证绑定包含以下信息:

  • uid:分配给请求用户的唯一身份用户 ID。
  • tokenAuthentication 收集的值映射。

如需详细了解 auth.token 的内容,请参阅身份验证令牌中的数据

response 绑定

response 绑定包含服务器在响应查询或更改时正在组装的数据

随着操作的进行,随着每个步骤成功完成,response 会包含成功完成的步骤的响应数据。

response 绑定的结构取决于其关联操作的形状,包括(多个)嵌套字段和(如果适用)嵌入式查询。

请注意,当您访问嵌入式查询响应数据时,字段可以包含任何数据类型,具体取决于嵌入式查询中请求的数据;当您访问 _insert_delete 等更改字段返回的数据时,这些字段可能包含 UUID 键、删除次数、null(请参阅更改参考文档)。

例如:

  • 在包含嵌入式查询的更改中,response 绑定包含 response.query.<fieldName>.<fieldName>....(在本例中为 response.query.todoListresponse.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
    }
  }
}
  • 在多步更改中(例如包含多个 _insert 字段),response 绑定包含 response.<fieldName>.<fieldName>....(在本例中为 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,
  })
}

this 绑定

绑定 this 的计算结果为 @check 指令附加到的字段。在基本情况下,您可以评估单值查询结果。

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

如果返回的字段因任何祖先都是列表而出现多次,则系统会使用绑定到每个值的 this 对每个出现情况进行测试。

对于任何给定路径,如果祖先为 null[],则不会到达该字段,并且系统会跳过对该路径的 CEL 评估。换句话说,只有当 thisnull 或非 null 时才会进行评估,而不会是 undefined

如果字段本身是列表或对象,this 会遵循相同的结构(包括在对象的情况下选择的所有后代),如以下示例所示。

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

复杂表达式语法

您可以通过与 &&|| 运算符组合来编写更复杂的表达式。

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

以下部分介绍了所有可用的运算符。

运算符和运算符优先级

请参考下表,了解运算符及其相应优先级。

给定任意表达式 ab、字段 f 和索引 i

运算符 说明 关联度
a[i] a() a.f 索引、调用、字段访问 从左到右
!a -a 一元否定 从右到左
a/b a%b a*b 乘法运算符 从左到右
a+b a-b 加法运算符 从左到右
a>b a>=b a<b a<=b 关系运算符 从左到右
a in b 存在于列表或映射中 从左到右
type(a) == t 类型比较,其中 t 可以是 bool、int、float、number、string、list、map、timestamp 或 duration 从左到右
a==b a!=b 比较运算符 从左到右
a && b 条件“与” 从左到右
a || b 条件“或” 从左到右
a ? true_value : false_value 三元表达式 从左到右

身份验证令牌中的数据

auth.token 对象可能包含以下值:

字段 说明
email 与账号关联的电子邮件地址(如果存在)。
email_verified 如果用户已验证他们可以访问 email 地址,则为 true。某些提供方会自动验证他们拥有的电子邮件地址。
phone_number 与账号关联的电话号码(如果有)。
name 用户的显示名(如果已设置)。
sub 用户的 Firebase UID。此 UID 在项目中是唯一的。
firebase.identities 与此用户账号关联的所有身份的字典。字典的键可以是以下任一值:emailphonegoogle.comfacebook.comgithub.comtwitter.com。字典的值是与账号关联的每个身份提供方的唯一标识符的数组。例如,auth.token.firebase.identities["google.com"][0] 包含与该账号关联的第一个 Google 用户 ID。
firebase.sign_in_provider 用于获取此令牌的登录服务提供方。可以是以下任一字符串:custompasswordphoneanonymousgoogle.comfacebook.comgithub.comtwitter.com
firebase.tenant 与账号关联的租户 ID(如果有)。例如 tenant2-m6tyz

JWT ID 令牌中的其他字段

您还可以访问以下 auth.token 字段:

自定义令牌声明
alg 算法 "RS256"
iss 颁发者 您项目的服务账号电子邮件地址
sub 主题 您项目的服务账号电子邮件地址
aud 受众 "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat 颁发时间 当前时间(与 UNIX 计时原点之间相隔的秒数)
exp 到期时间 令牌到期的时间(与 UNIX 计时原点之间相隔的秒数),该时间可能比 iat 晚最多 3600 秒
注意:这仅会控制自定义令牌本身的过期时间。但是,一旦您使用 signInWithCustomToken() 让用户登录,他们将一直在设备上保持登录状态,直到其会话失效或用户退出账号为止。
<claims>(可选) 要包含在令牌中的可选自定义声明,可通过表达式中的 auth.token(或 request.auth.token)访问。例如,如果您创建自定义声明 adminClaim,则可以使用 auth.token.adminClaim 访问该声明。