Skip to content

Commit

Permalink
Add ApolloCompilerPlugin.schemaDocumentListener() (#6165)
Browse files Browse the repository at this point in the history
* Add ApolloCompilerPlugin.schemaDocumentListener()

* fix integration tests
  • Loading branch information
martinbonnin authored Sep 30, 2024
1 parent df6ab99 commit 16fa109
Show file tree
Hide file tree
Showing 23 changed files with 382 additions and 56 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.apollographql.apollo.ast

import com.apollographql.apollo.annotations.ApolloExperimental

/**
* A known foreign schema
*
* @param name the name of the foreign schema as in https://specs.apollo.dev/link/v1.0/#@link.url
* @param version the version of the foreign schema as in https://specs.apollo.dev/link/v1.0/#@link.url
* @param definitions the definitions in the foreign schema
* @param directivesToStrip the name of directives that must be stripped before being sent to the server
* without the leading '@'.
* For an example: `"catch"`
*/
@ApolloExperimental
class ForeignSchema(
val name: String,
val version: String,
val definitions: List<GQLDefinition>,
val directivesToStrip: List<String> = definitions.filterIsInstance<GQLDirective>().map { it.name },
)
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ fun GQLDocument.validateAsSchemaAndAddApolloDefinition(): GQLResult<Schema> {
definitions,
SchemaValidationOptions(
true,
supportedForeignSchemas()
builtinForeignSchemas()
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import com.apollographql.apollo.annotations.ApolloDeprecatedSince
import com.apollographql.apollo.annotations.ApolloExperimental
import com.apollographql.apollo.annotations.ApolloInternal
import com.apollographql.apollo.ast.internal.ExtensionsMerger
import com.apollographql.apollo.ast.internal.ForeignSchema
import com.apollographql.apollo.ast.internal.builtinsDefinitionsStr
import com.apollographql.apollo.ast.internal.ensureSchemaDefinition
import com.apollographql.apollo.ast.internal.kotlinLabsDefinitions_0_3
Expand Down Expand Up @@ -103,7 +102,7 @@ fun kotlinLabsDefinitions(version: String): List<GQLDefinition> {
* This is exported in case users want to validate documents meant for Apollo Kotlin.
*/
@ApolloExperimental
fun supportedForeignSchemas(): List<ForeignSchema> {
fun builtinForeignSchemas(): List<ForeignSchema> {
return listOf(
ForeignSchema("kotlin_labs", "v0.2", kotlinLabsDefinitions("v0.2"), listOf("optional", "nonnull")),
ForeignSchema("kotlin_labs", "v0.3", kotlinLabsDefinitions("v0.3"), listOf("optional", "nonnull")),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.apollographql.apollo.annotations.ApolloExperimental
import com.apollographql.apollo.annotations.ApolloInternal
import com.apollographql.apollo.ast.ConflictResolution
import com.apollographql.apollo.ast.DirectiveRedefinition
import com.apollographql.apollo.ast.ForeignSchema
import com.apollographql.apollo.ast.GQLDefinition
import com.apollographql.apollo.ast.GQLDirective
import com.apollographql.apollo.ast.GQLDirectiveDefinition
Expand Down Expand Up @@ -285,24 +286,6 @@ private class Import(
val newNames: Map<String, String>,
)

/**
* A known foreign schema
*
* @param name the name of the foreign schema as in https://specs.apollo.dev/link/v1.0/#@link.url
* @param version the version of the foreign schema as in https://specs.apollo.dev/link/v1.0/#@link.url
* @param definitions the definitions in the foreign schema
* @param directivesToStrip the name of directives that must be stripped before being sent to the server
* without the leading '@'.
* For an example: `"catch"`
*/
@ApolloExperimental
class ForeignSchema(
val name: String,
val version: String,
val definitions: List<GQLDefinition>,
val directivesToStrip: List<String> = definitions.filterIsInstance<GQLDirective>().map { it.name },
)

private class UrlParseResult(val name: String, val version: String)

private fun String.parseLink(): UrlParseResult? {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
package com.apollographql.apollo.graphql.ast.test

import com.apollographql.apollo.ast.GQLDirectiveDefinition
import com.apollographql.apollo.ast.GQLDirectiveLocation
import com.apollographql.apollo.ast.GQLInputValueDefinition
import com.apollographql.apollo.ast.internal.ForeignSchema
import com.apollographql.apollo.ast.ForeignSchema
import com.apollographql.apollo.ast.internal.SchemaValidationOptions
import com.apollographql.apollo.ast.parseAsGQLDocument
import com.apollographql.apollo.ast.toGQLDocument
import com.apollographql.apollo.ast.toSchema
import com.apollographql.apollo.ast.validateAsSchema
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue

class SchemaTest {
@Test
Expand Down
2 changes: 2 additions & 0 deletions libraries/apollo-compiler/api/apollo-compiler.api
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,9 @@ public final class com/apollographql/apollo/compiler/AdapterInitializer$Companio
public final class com/apollographql/apollo/compiler/ApolloCompiler {
public static final field INSTANCE Lcom/apollographql/apollo/compiler/ApolloCompiler;
public final fun buildCodegenSchema (Ljava/util/List;Lcom/apollographql/apollo/compiler/ApolloCompiler$Logger;Lcom/apollographql/apollo/compiler/CodegenSchemaOptions;)Lcom/apollographql/apollo/compiler/CodegenSchema;
public final fun buildCodegenSchema (Ljava/util/List;Lcom/apollographql/apollo/compiler/ApolloCompiler$Logger;Lcom/apollographql/apollo/compiler/CodegenSchemaOptions;Ljava/util/List;)Lcom/apollographql/apollo/compiler/CodegenSchema;
public final fun buildIrOperations (Lcom/apollographql/apollo/compiler/CodegenSchema;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lcom/apollographql/apollo/compiler/IrOptions;Lcom/apollographql/apollo/compiler/DocumentTransform;Lcom/apollographql/apollo/compiler/ApolloCompiler$Logger;)Lcom/apollographql/apollo/compiler/ir/IrOperations;
public final fun buildSchemaAndOperationsSources (Lcom/apollographql/apollo/compiler/CodegenSchema;Ljava/util/List;Lcom/apollographql/apollo/compiler/IrOptions;Lcom/apollographql/apollo/compiler/CodegenOptions;Lcom/apollographql/apollo/compiler/LayoutFactory;Lcom/apollographql/apollo/compiler/OperationOutputGenerator;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/DocumentTransform;Lcom/apollographql/apollo/compiler/ApolloCompiler$Logger;Ljava/io/File;)Lcom/apollographql/apollo/compiler/codegen/SourceOutput;
public final fun buildSchemaAndOperationsSources (Ljava/util/List;Ljava/util/List;Lcom/apollographql/apollo/compiler/CodegenSchemaOptions;Lcom/apollographql/apollo/compiler/IrOptions;Lcom/apollographql/apollo/compiler/CodegenOptions;Lcom/apollographql/apollo/compiler/LayoutFactory;Lcom/apollographql/apollo/compiler/OperationOutputGenerator;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/DocumentTransform;Lcom/apollographql/apollo/compiler/ApolloCompiler$Logger;Ljava/io/File;)Lcom/apollographql/apollo/compiler/codegen/SourceOutput;
public final fun buildSchemaAndOperationsSourcesFromIr (Lcom/apollographql/apollo/compiler/CodegenSchema;Lcom/apollographql/apollo/compiler/ir/IrOperations;Lcom/apollographql/apollo/compiler/UsedCoordinates;Ljava/util/List;Lcom/apollographql/apollo/compiler/CodegenOptions;Lcom/apollographql/apollo/compiler/codegen/SchemaAndOperationsLayout;Lcom/apollographql/apollo/compiler/OperationOutputGenerator;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/Transform;Ljava/io/File;)Lcom/apollographql/apollo/compiler/codegen/SourceOutput;
public final fun buildSchemaSources (Lcom/apollographql/apollo/compiler/CodegenSchema;Lcom/apollographql/apollo/compiler/UsedCoordinates;Lcom/apollographql/apollo/compiler/CodegenOptions;Lcom/apollographql/apollo/compiler/codegen/SchemaLayout;Lcom/apollographql/apollo/compiler/Transform;Lcom/apollographql/apollo/compiler/Transform;)Lcom/apollographql/apollo/compiler/codegen/SourceOutput;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ package com.apollographql.apollo.compiler
import com.apollographql.apollo.ast.DeprecatedUsage
import com.apollographql.apollo.ast.DifferentShape
import com.apollographql.apollo.ast.DirectiveRedefinition
import com.apollographql.apollo.ast.ForeignSchema
import com.apollographql.apollo.ast.GQLDefinition
import com.apollographql.apollo.ast.GQLDirectiveDefinition
import com.apollographql.apollo.ast.GQLDocument
import com.apollographql.apollo.ast.GQLFragmentDefinition
import com.apollographql.apollo.ast.GQLOperationDefinition
Expand All @@ -13,21 +13,20 @@ import com.apollographql.apollo.ast.GQLSchemaDefinition
import com.apollographql.apollo.ast.GQLTypeDefinition
import com.apollographql.apollo.ast.IncompatibleDefinition
import com.apollographql.apollo.ast.Issue
import com.apollographql.apollo.ast.KOTLIN_LABS_VERSION
import com.apollographql.apollo.ast.ParserOptions
import com.apollographql.apollo.ast.QueryDocumentMinifier
import com.apollographql.apollo.ast.Schema
import com.apollographql.apollo.ast.UnknownDirective
import com.apollographql.apollo.ast.UnusedFragment
import com.apollographql.apollo.ast.UnusedVariable
import com.apollographql.apollo.ast.checkEmpty
import com.apollographql.apollo.ast.kotlinLabsDefinitions
import com.apollographql.apollo.ast.internal.SchemaValidationOptions
import com.apollographql.apollo.ast.parseAsGQLDocument
import com.apollographql.apollo.ast.pretty
import com.apollographql.apollo.ast.builtinForeignSchemas
import com.apollographql.apollo.ast.toGQLDocument
import com.apollographql.apollo.ast.validateAsExecutable
import com.apollographql.apollo.ast.validateAsSchemaAndAddApolloDefinition
import com.apollographql.apollo.compiler.codegen.LayoutImpl
import com.apollographql.apollo.ast.validateAsSchema
import com.apollographql.apollo.compiler.codegen.SchemaAndOperationsLayout
import com.apollographql.apollo.compiler.codegen.SchemaLayout
import com.apollographql.apollo.compiler.codegen.SourceOutput
Expand All @@ -49,7 +48,6 @@ import com.apollographql.apollo.compiler.ir.IrOperations
import com.apollographql.apollo.compiler.ir.IrOperationsBuilder
import com.apollographql.apollo.compiler.ir.IrSchema
import com.apollographql.apollo.compiler.ir.IrSchemaBuilder
import com.apollographql.apollo.compiler.ir.IrTargetObject
import com.apollographql.apollo.compiler.operationoutput.OperationDescriptor
import com.apollographql.apollo.compiler.pqm.toPersistedQueryManifest
import java.io.File
Expand All @@ -63,6 +61,20 @@ object ApolloCompiler {
schemaFiles: List<InputFile>,
logger: Logger?,
codegenSchemaOptions: CodegenSchemaOptions,
): CodegenSchema {
return buildCodegenSchema(
schemaFiles,
logger,
codegenSchemaOptions,
emptyList()
)
}

fun buildCodegenSchema(
schemaFiles: List<InputFile>,
logger: Logger?,
codegenSchemaOptions: CodegenSchemaOptions,
foreignSchemas: List<ForeignSchema>,
): CodegenSchema {
val schemaDocuments = schemaFiles.map {
it.normalizedPath to it.file.toGQLDocument(allowJson = true)
Expand Down Expand Up @@ -106,10 +118,15 @@ object ApolloCompiler {
sourceLocation = null
)

/**
* TODO: use `validateAsSchema` to not automatically add the apollo definitions
*/
val result = schemaDocument.validateAsSchemaAndAddApolloDefinition()
val result = schemaDocument.validateAsSchema(
validationOptions = SchemaValidationOptions(
/**
* TODO: switch to false
*/
addKotlinLabsDefinitions = true,
builtinForeignSchemas() + foreignSchemas
)
)

val issueGroup = result.issues.group(warnOnDeprecatedUsages = true, fieldsOnDisjointTypesMustMerge = true)

Expand Down Expand Up @@ -485,9 +502,44 @@ object ApolloCompiler {
val codegenSchema = buildCodegenSchema(
schemaFiles = schemaFiles,
logger = logger,
codegenSchemaOptions = codegenSchemaOptions
codegenSchemaOptions = codegenSchemaOptions,
foreignSchemas = emptyList()
)

return buildSchemaAndOperationsSources(
codegenSchema,
executableFiles,
irOptions,
codegenOptions,
layoutFactory,
operationOutputGenerator,
irOperationsTransform,
javaOutputTransform,
kotlinOutputTransform,
documentTransform,
logger,
operationManifestFile
)
}


/**
* Compiles a set of files without serializing the intermediate results
*/
fun buildSchemaAndOperationsSources(
codegenSchema: CodegenSchema,
executableFiles: List<InputFile>,
irOptions: IrOptions,
codegenOptions: CodegenOptions,
layoutFactory: LayoutFactory?,
@Suppress("DEPRECATION") operationOutputGenerator: OperationOutputGenerator?,
irOperationsTransform: Transform<IrOperations>?,
javaOutputTransform: Transform<JavaOutput>?,
kotlinOutputTransform: Transform<KotlinOutput>?,
documentTransform: DocumentTransform?,
logger: Logger?,
operationManifestFile: File?,
): SourceOutput {
val irOperations = buildIrOperations(
codegenSchema = codegenSchema,
executableFiles = executableFiles,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package com.apollographql.apollo.compiler

import com.apollographql.apollo.annotations.ApolloExperimental
import com.apollographql.apollo.ast.ForeignSchema
import com.apollographql.apollo.ast.GQLDocument
import com.apollographql.apollo.ast.GQLFragmentDefinition
import com.apollographql.apollo.ast.GQLOperationDefinition
import com.apollographql.apollo.ast.Schema
Expand All @@ -10,13 +12,15 @@ import com.apollographql.apollo.compiler.codegen.kotlin.KotlinOutput
import com.apollographql.apollo.compiler.ir.IrOperations
import com.apollographql.apollo.compiler.operationoutput.OperationDescriptor
import com.apollographql.apollo.compiler.operationoutput.OperationId
import java.io.File

/**
* [ApolloCompilerPlugin] allows to customize the behaviour of the Apollo Compiler.
*
* [ApolloCompilerPlugin] is run in an isolated classloader. You may throw from [ApolloCompilerPlugin] methods to fail the build
* but custom exception classes are not accessible from the calling environment like Gradle for an example.
* Prefer throwing regular Java exception classes.
* [ApolloCompilerPlugin] may be instantiated several times in a codegen run. Each instance is create in a
* separate classloader.
* The classloaders contains `apollo-compiler` classes and the runtime classpath of the [ApolloCompilerPlugin].
* You may throw from [ApolloCompilerPlugin] methods to fail the build.
*/
interface ApolloCompilerPlugin {
/**
Expand Down Expand Up @@ -65,8 +69,37 @@ interface ApolloCompilerPlugin {
fun irOperationsTransform(): Transform<IrOperations>? {
return null
}

/**
* @return A list of [ForeignSchema] supported by this plugin
*/
@ApolloExperimental
fun foreignSchemas(): List<ForeignSchema> {
return emptyList()
}

/**
* @return A [SchemaDocumentListener] called whenever the schema changed
*/
@ApolloExperimental
fun schemaDocumentListener(): SchemaDocumentListener? {
return null
}
}

@ApolloExperimental
interface SchemaDocumentListener {
/**
* Called when the schema changed and codegen needs to be updated
*
* @param schema the validated schema document.
* @param outputDirectory the compiler output directory. This directory is shared with the compiler, make sure to use a specific
* package name to avoid clobbering other files.
*/
fun onSchemaDocument(schema: GQLDocument, outputDirectory: File)
}


/**
* A [DocumentTransform] transforms operations and fragments at build time. [DocumentTransform] can add or remove fields automatically for an example.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,8 @@ private fun ApolloCompiler.buildSchemaAndOperationsSourcesAndReturnIrOperations(
val codegenSchema = buildCodegenSchema(
schemaFiles = schemaFiles,
logger = logger,
codegenSchemaOptions = codegenSchemaOptions
codegenSchemaOptions = codegenSchemaOptions,
foreignSchemas = emptyList()
)

val irOperations = buildIrOperations(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class MetadataTest {
schemaFiles = setOf(File("src/test/metadata/schema.graphqls")).toInputFiles(),
logger = null,
codegenSchemaOptions = codegenSchemaOptionsFile.toCodegenSchemaOptions(),
foreignSchemas = emptyList(),
).writeTo(codegenSchemaFile)

ApolloCompiler.buildIrOperations(
Expand Down
Loading

0 comments on commit 16fa109

Please sign in to comment.