Apollo AST
要生成客户端代码, Apollo Kotlin 解析您的 GraphQL模式和每一个 操作成为一个 抽象语法树AST). 一个 AST 表示了一个 GraphQL 文档 以类型安全的、机器可读的格式。
Apollo Kotlin 解析器有其自己的工件(apollo-ast
),你可以独立于 apollo-runtime
或 apollo-api
使用。
Apollo-ast 的特性包括:
- 将模式和操作文档解析为抽象语法树(AST)。
- 提供 输入验证以引发警告和错误(参见 GraphQL 规范)
- 支持将 AST 输出为有效的缩进符合 GraphQL 文档
- 支持通过
transform
API 操作 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())