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。
  • token:由 Authentication 收集的值的映射。

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

response 绑定

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

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

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

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

例如:

  • 在包含嵌入式查询的 mutation 中,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 访问该声明。