Apollo AST
要生成客户端代码, Apollo Kotlin 解析您的 GraphQL模式和每一个 操作成为一个 抽象语法树AST). 一个 AST 表示了一个 GraphQL 文档 以类型安全的、机器可读的格式。
Apollo Kotlin 解析器有其自己的工件(apollo-ast),你可以独立于 apollo-runtime 或 apollo-api 使用。
Apollo-ast 的特性包括:
- 将模式和操作文档解析为抽象语法树(AST)。
- 提供 输入验证以引发警告和错误(参见 GraphQL 规范)
- 支持将 AST 输出为有效的缩进符合 GraphQL 文档
- 支持通过
transformAPI 操作 AST
安装
将 apollo-ast 依赖项添加到你的项目中:
dependencies {// ...implementation("com.apollographql.apollo:apollo-ast:4.0.0")}
解析文档
使用 parseAsGQLDocument 方法从 File、String 或 Okio BufferedSource 解析文档。
val graphQLText = """query HeroForEpisode(${"$"}ep: Episode) {hero(episode: ${"$"}ep) {namefriends {height}foobar}}""".trimIndent()val parseResult = graphQLText.parseAsGQLDocument()
此方法返回一个 GQLResult<GQLDocument>,其中包含 document 和/或解析问题,每个问题都可以有 WARNING 或 ERROR的严重程度。因为可能存在警告,所以可能同时存在一个有效的 document 和问题。
要获取 document 并在出现错误时抛出异常,请使用 getOrThrow():
val queryGqlDocument = parseResult.getOrThrow()
GQLDocument 是 AST 的根。它包含一个 GQLDefinition 的列表,这些定义共同表示 GraphQL document。
AST 中的所有节点都是 GQLNode 的子类(所有名称都以 GQL 前缀开头)。每个子类都公开与相应节点类型相关的特定属性和方法。
以下是 AST 结构的示例
在上面的 HeroForEpisode 示例 中,以下是由解析器返回的 AST 结构:
GQLDocument└─GQLOperationDefinition query "HeroForEpisode"├─GQLVariableDefinition "ep": "Episode"└─GQLSelectionSet└─GQLField "hero"├─GQLSelectionSet│ ├─GQLField "name"│ ├─GQLField "friends"│ │ └─GQLSelectionSet│ │ └─GQLField "height"│ └─GQLField "foobar"└─GQLArguments└─GQLArgument "episode"└─GQLVariableValue "ep"
请注意,此结构与节点名称紧密遵循 GraphQL 规范。
验证输入
除了解析之外,apollo-ast 库还提供了用于执行 GraphQL 文档的高级验证的方法。
要验证解析后的 GQLDocument:
- 如果文档表示一个模式(schema),请调用 validateAsSchema方法。
- 如果文档表示一个或多个操作(operations),请调用 validateAsExecutable方法。
validateAsSchema
validateAsSchema返回一个 GQLResult<Schema>。以下代码段解析并验证一个使用未定义指令(directive)(@private)的无效模式:
val schemaText = """type Query {hero(episode: Episode): Character}enum Episode {NEWHOPEEMPIRE}type Character @private {name: Stringheight: Int @deprecatedfriends: [Character]}""".trimIndent()val schemaGQLDocument = schemaText.parseAsGQLDocument().getOrThrow()val schemaResult = schemaGQLDocument.validateAsSchema()println(schemaResult.issues.map { it.severity.name + ": " + it.message })
在执行时,此代码段将打印 [WARNING: Unknown directive 'private']。
因为这个是警告而不是错误,您仍然可以使用返回的 schemaResult.getOrThrow()
validateAsExecutable
validateAsExecutable 方法检查一个 document 的定义操作是否与提供的特定 Schema 有效。您可以通过调用代表 sch...
val schema = schemaGQLDocument.validateAsSchema().getOrThrow()val executableIssues = queryGqlDocument.validateAsExecutable(schema)println(executableIssues.map { it.severity.name + ": " + it.message })
如果queryGqlDocument查询了一个过时的字段并且拼写错误了另一个,这个片段可能打印以下内容:
[WARNING: Use of deprecated field 'height', ERROR: Can't query 'frends' on type 'Character']
输出SDL
您可以使用GQLDocument的standard GraphQL语法输出,使用toUtf8 扩展:
// Returns a stringprintln(queryGqlDocument.toUtf8())// Output to a FilequeryGqlDocument.toUtf8(file)
转换AST
您可以使用GQLDocument的transform方法修改现有的AST。
您传递一个接受GQLNode的lambda,并返回用于操作AST的指令的transform:
Continue:保留节点并继续访问子节点Delete:删除节点Replace(GQLNode):用指定的节点替换节点
该transform方法遍历AST,并对每个节点执行lambda,然后根据lambda的返回值在AST上执行操作。
注意,Delete和Replace,节点的不自动访问子节点,因此如果需要,您应该递归地调用transform。
例如,此代码片段从字段中移除所有restrictedField,定义在queryGqlDocument中的操作,并打印结果:
val transformedQuery = queryGqlDocument.transform{ node ->if (node is GQLField && node.name == "restrictedField") {TransformResult.Delete} else {TransformResult.Continue}}println(transformedQuery!!.toUtf8())