Apollo AST
为了生成客户端代码,Apollo Kotlin 解析您的 GraphQL模式 以及 它所对应的操作,并将它们转换为抽象语法树(AST) (AST)。AST以类型安全、 machine-readable format 的形式表示一个 GraphQL 文档。
apollo-kotlin的解析器拥有自己的模块(apollo-ast
),您可以独立于apollo-runtime
或apollo-api
使用。
apollo-ast的功能包括:
- 解析模式文档和操作文档为抽象语法树(AST)
- 提供输入验证来发出警告和错误(参见GraphQL 规范)
- 支持将AST输出为有效的、缩进的GraphQL文档
- 支持通过
transform
API操作AST
安装
将apollo-ast
依赖添加到您的项目:
dependencies {// ...implementation("com.apollographql.apollo3:apollo-ast:3.8.5")}
解析文档
使用parseAsGQLDocument
方法从File
、String
或Okio BufferedSource
中解析文档。
val graphQLText = """query HeroForEpisode(${"$"}ep: Episode) {hero(episode: ${"$"}ep) {namefriends {height}foobar}}""".trimIndent()val parseResult = Buffer().writeUtf8(graphQLText).parseAsGQLDocument()
该方法返回一个GQLResult<GQLDocument>
,其中包含文档和/或解析问题,每个问题都可以具有WARNING
或ERROR
的严重性。由于可能有警告,因此可能同时具有有效的文档和问题。
要获取文档并在出错时抛出,请使用valueAssertNoErrors()
:
val queryGqlDocument = parseResult.valueAssertNoErrors()
GQLDocument
是AST的根。它包含代表GraphQL文档的GQLDefinition列表。
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
:
- 如果文档代表一个模式,请调用
validateAsSchema
方法。 - 如果文档代表一个或多个操作,请调用
validateAsExecutable
方法。
validateAsSchema
validateAsSchema
返回一个 GQLResult<Schema>
. 以下代码片段解析并验证了一个使用未定义的 指令(@private
)的短无效方案:
val schemaText = """type Query {hero(episode: Episode): Character}enum Episode {NEWHOPEEMPIRE}type Character @private {name: Stringheight: Int @deprecatedfriends: [Character]}""".trimIndent()val schemaGQLDocument = Buffer().writeUtf8(schemaText).parseAsGQLDocument().valueAssertNoErrors()val schemaResult = schemaGQLDocument.validateAsSchema()println(schemaResult.issues.map { it.severity.name + ": " + it.message })
当执行此代码片段时,将打印 [WARNING: Unknown directive 'private']
。
因为这只是一个警告而不是错误,您仍然可以使用返回的 schemaResult.valueAssertNoErrors()
validateAsExecutable
validateAsExecutable
方法检查文档定义的操作是否与提供的特定 Schema
有效。您可以调用代表该方案的 GQLDocument
validateAsSchema 方法来获取此 Schema
参数:
val schema = schemaGQLDocument.validateAsSchema().valueAssertNoErrors()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
您可以使用 toUtf8
将 GQLDocument
输出为标准的 GraphQL 语法
// Returns a stringprintln(queryGqlDocument.toUtf8())// Output to a FilequeryGqlDocument.toUtf8(file)// Output to an Okio BufferedSinkqueryGqlDocument.toUtf8(sink)
转换 AST
您可以使用
GQLDocument
的 transform
方法来修改现有的 AST。
您传递transform
一个接受GQLNode
作为参数并返回用以操作AST指令的lambda
表达式:
Continue
:保持节点不变并继续访问子节点Delete
:删除节点Replace(GQLNode)
:将节点替换为给定的节点
该方法会遍历AST并执行对于每个节点的lambda表达式,然后根据lambda的返回值在AST上执行操作。
需要注意的是,使用Delete
和Replace
时,不会自动访问节点的子节点,因此在需要时请递归调用transform
。
例如,以下代码段从在queryGqlDocument
中定义的操作中移除所有名为fields的restrictedField
,并打印结果:
val transformedQuery = queryGqlDocument.transform{ node ->if (node is GQLField && node.name == "restrictedField") {TransformResult.Delete} else {TransformResult.Continue}}println(transformedQuery!!.toUtf8())