Skip to content

Commit

Permalink
[⚙️compiler] Open up IR and add integration test for IR transform (#5604
Browse files Browse the repository at this point in the history
)

* open up the IrOperations classes (@ApolloExperimental)

* Add test for IR transforms

* irTransform is now experimental too

* update api dump
  • Loading branch information
martinbonnin committed Feb 7, 2024
1 parent 6e5f101 commit a76eed5
Show file tree
Hide file tree
Showing 18 changed files with 685 additions and 74 deletions.
402 changes: 397 additions & 5 deletions libraries/apollo-compiler/api/apollo-compiler.api

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ import com.apollographql.apollo3.compiler.internal.checkApolloTargetNameClashes
import com.apollographql.apollo3.compiler.internal.checkCapitalizedFields
import com.apollographql.apollo3.compiler.internal.checkConditionalFragments
import com.apollographql.apollo3.compiler.internal.checkKeyFields
import com.apollographql.apollo3.compiler.ir.DefaultIrOperations
import com.apollographql.apollo3.compiler.ir.IrOperations
import com.apollographql.apollo3.compiler.ir.IrOperationsBuilder
import com.apollographql.apollo3.compiler.ir.IrSchema
Expand Down Expand Up @@ -371,10 +370,8 @@ object ApolloCompiler {
kotlinOutputTransform: Transform<KotlinOutput>?,
operationManifestFile: File?,
): SourceOutput {
check(irOperations is DefaultIrOperations)

@Suppress("NAME_SHADOWING")
val irOperations = irOperations.maybeTransform(irOperationsTransform) as DefaultIrOperations
val irOperations = irOperations.maybeTransform(irOperationsTransform)

val targetLanguage = defaultTargetLanguage(codegenOptions.targetLanguage, upstreamCodegenMetadata)
codegenOptions.validate()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.apollographql.apollo3.compiler

import com.apollographql.apollo3.annotations.ApolloExperimental
import com.apollographql.apollo3.compiler.codegen.SchemaAndOperationsLayout
import com.apollographql.apollo3.compiler.codegen.java.JavaOutput
import com.apollographql.apollo3.compiler.codegen.kotlin.KotlinOutput
Expand Down Expand Up @@ -32,6 +33,7 @@ interface Plugin {
/**
* @return the [Transform] to be applied to [IrOperations] or null to use the default [Transform]
*/
@ApolloExperimental
fun irOperationsTransform(): Transform<IrOperations>? {
return null
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ import com.apollographql.apollo3.compiler.defaultGenerateQueryDocument
import com.apollographql.apollo3.compiler.defaultGenerateSchema
import com.apollographql.apollo3.compiler.defaultNullableFieldStyle
import com.apollographql.apollo3.compiler.generateMethodsJava
import com.apollographql.apollo3.compiler.ir.DefaultIrOperations
import com.apollographql.apollo3.compiler.ir.DefaultIrSchema
import com.apollographql.apollo3.compiler.ir.IrOperations
import com.apollographql.apollo3.compiler.ir.IrSchema
Expand Down Expand Up @@ -210,8 +209,6 @@ internal object JavaCodegen {
layout: OperationsLayout,
javaOutputTransform: Transform<JavaOutput>?,
): JavaOutput {
check(irOperations is DefaultIrOperations)

if (irOperations.codegenModels != MODELS_OPERATION_BASED) {
error("Java codegen does not support ${irOperations.codegenModels}. Only $MODELS_OPERATION_BASED is supported.")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ import com.apollographql.apollo3.compiler.defaultJsExport
import com.apollographql.apollo3.compiler.defaultRequiresOptInAnnotation
import com.apollographql.apollo3.compiler.defaultSealedClassesForEnumsMatching
import com.apollographql.apollo3.compiler.generateMethodsKotlin
import com.apollographql.apollo3.compiler.ir.DefaultIrOperations
import com.apollographql.apollo3.compiler.ir.DefaultIrSchema
import com.apollographql.apollo3.compiler.ir.IrOperations
import com.apollographql.apollo3.compiler.ir.IrSchema
Expand Down Expand Up @@ -224,8 +223,6 @@ internal object KotlinCodegen {
layout: OperationsLayout,
kotlinOutputTransform: Transform<KotlinOutput>?,
): KotlinOutput {
check(irOperations is DefaultIrOperations)

val generateDataBuilders = codegenSchema.generateDataBuilders
val flatten = irOperations.flattenModels

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.apollographql.apollo3.compiler.ir

import com.apollographql.apollo3.annotations.ApolloExperimental
import kotlin.reflect.KClass

/**
Expand All @@ -8,20 +9,24 @@ import kotlin.reflect.KClass
* @param T the type of the variable elements. This allows representing BooleanExpression that only contain variables and
* other that may also contain possibleTypes
*/
internal sealed class BooleanExpression<out T : Any> {
@ApolloExperimental
sealed class BooleanExpression<out T : Any> {
/**
* This is not super well defined but works well enough for our simple use cases
*/
abstract fun simplify(): BooleanExpression<T>

@ApolloExperimental
object True : BooleanExpression<Nothing>() {
override fun simplify() = this
}

@ApolloExperimental
object False : BooleanExpression<Nothing>() {
override fun simplify() = this
}

@ApolloExperimental
data class Not<out T : Any>(val operand: BooleanExpression<T>) : BooleanExpression<T>() {
override fun simplify() = when (this.operand) {
is True -> False
Expand All @@ -30,6 +35,7 @@ internal sealed class BooleanExpression<out T : Any> {
}
}

@ApolloExperimental
data class Or<T : Any>(val operands: Set<BooleanExpression<T>>) : BooleanExpression<T>() {
constructor(vararg operands: BooleanExpression<T>) : this(operands.toSet())

Expand All @@ -56,6 +62,7 @@ internal sealed class BooleanExpression<out T : Any> {
override fun toString() = operands.joinToString(" | ")
}

@ApolloExperimental
data class And<T : Any>(val operands: Set<BooleanExpression<T>>) : BooleanExpression<T>() {
constructor(vararg operands: BooleanExpression<T>) : this(operands.toSet())

Expand All @@ -80,6 +87,7 @@ internal sealed class BooleanExpression<out T : Any> {
}
}

@ApolloExperimental
data class Element<out T : Any>(
val value: T,
) : BooleanExpression<T>() {
Expand All @@ -94,22 +102,26 @@ internal fun <T : Any> not(other: BooleanExpression<T>): BooleanExpression<T> =
/**
* A generic term in a [BooleanExpression]
*/
internal sealed class BTerm
@ApolloExperimental
sealed class BTerm

/**
* A term that comes from @include/@skip or @defer directives and that needs to be matched against operation variables
*/
internal data class BVariable(val name: String) : BTerm()
@ApolloExperimental
data class BVariable(val name: String) : BTerm()

/**
* A term that comes from @defer directives and that needs to be matched against label and current JSON path
*/
internal data class BLabel(val label: String?) : BTerm()
@ApolloExperimental
data class BLabel(val label: String?) : BTerm()

/**
* A term that comes from a fragment type condition and that needs to be matched against __typename
*/
internal data class BPossibleTypes(val possibleTypes: Set<String>) : BTerm() {
@ApolloExperimental
data class BPossibleTypes(val possibleTypes: Set<String>) : BTerm() {
constructor(vararg types: String) : this(types.toSet())
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.apollographql.apollo3.compiler.ir

import com.apollographql.apollo3.annotations.ApolloExperimental
import com.apollographql.apollo3.ast.GQLFragmentDefinition
import com.apollographql.apollo3.ast.GQLType
import com.apollographql.apollo3.compiler.internal.BooleanExpressionSerializer
Expand Down Expand Up @@ -28,26 +29,22 @@ import kotlinx.serialization.json.Json
* found in the IR are as found in the GraphQL documents
*/
@Serializable
internal data class DefaultIrOperations(
@ApolloExperimental
data class IrOperations(
val operations: List<IrOperation>,
val fragments: List<IrFragmentDefinition>,
override val usedFields: Map<String, Set<String>>,
val usedFields: Map<String, Set<String>>,

val flattenModels: Boolean,
val decapitalizeFields: Boolean,
override val codegenModels: String,
val codegenModels: String,

override val fragmentDefinitions: List<@Serializable(with = GQLFragmentDefinitionSerializer::class) GQLFragmentDefinition>,
) : IrOperations

interface IrOperations {
val fragmentDefinitions: List<GQLFragmentDefinition>
val usedFields: Map<String, Set<String>>
val codegenModels: String
}
val fragmentDefinitions: List<@Serializable(with = GQLFragmentDefinitionSerializer::class) GQLFragmentDefinition>,
)

@Serializable
internal data class IrOperation(
@ApolloExperimental
data class IrOperation(
val name: String,
val operationType: IrOperationType,
val typeCondition: String,
Expand All @@ -59,14 +56,14 @@ internal data class IrOperation(
*/
val sourceWithFragments: String,
val normalizedFilePath: String,
val responseBasedDataModelGroup: IrModelGroup?,
val dataProperty: IrProperty,
val dataModelGroup: IrModelGroup,
val ignoreErrors: Boolean,
)

@Serializable
internal data class IrSelectionSet(
@ApolloExperimental
data class IrSelectionSet(
/**
* a name for this [IrSelectionSet]. This name is unique across all [IrSelectionSet] for a given operation/fragment definition
*/
Expand All @@ -79,11 +76,12 @@ internal data class IrSelectionSet(
)

@Serializable
internal sealed interface IrSelection
sealed interface IrSelection

@Serializable
@SerialName("field")
internal data class IrField(
@ApolloExperimental
data class IrField(
val name: String,
val alias: String?,
val type: IrTypeRef,
Expand All @@ -94,7 +92,8 @@ internal data class IrField(
) : IrSelection

@Serializable
internal data class IrArgument(
@ApolloExperimental
data class IrArgument(
val name: String,
/**
* The value for this argument. May be null if the argument is absent
Expand All @@ -105,20 +104,25 @@ internal data class IrArgument(
)

@Serializable
internal sealed interface IrTypeRef
@ApolloExperimental
sealed interface IrTypeRef
@Serializable
@SerialName("nonnull")
internal data class IrNonNullTypeRef(val ofType: IrTypeRef) : IrTypeRef
@ApolloExperimental
data class IrNonNullTypeRef(val ofType: IrTypeRef) : IrTypeRef
@Serializable
@SerialName("list")
internal data class IrListTypeRef(val ofType: IrTypeRef) : IrTypeRef
@ApolloExperimental
data class IrListTypeRef(val ofType: IrTypeRef) : IrTypeRef
@Serializable
@SerialName("named")
internal data class IrNamedTypeRef(val name: String) : IrTypeRef
@ApolloExperimental
data class IrNamedTypeRef(val name: String) : IrTypeRef

@Serializable
@SerialName("fragment")
internal data class IrFragment(
@ApolloExperimental
data class IrFragment(
val typeCondition: String,
val possibleTypes: List<String>,
@Serializable(with = BooleanExpressionSerializer::class)
Expand All @@ -135,7 +139,8 @@ internal data class IrFragment(
) : IrSelection

@Serializable
internal data class IrFragmentDefinition(
@ApolloExperimental
data class IrFragmentDefinition(
val name: String,
val description: String?,
val filePath: String,
Expand All @@ -158,7 +163,8 @@ internal data class IrFragmentDefinition(
)

@Serializable
internal sealed interface IrOperationType {
@ApolloExperimental
sealed interface IrOperationType {
val typeName: String

val name: String
Expand All @@ -172,12 +178,15 @@ internal sealed interface IrOperationType {

@Serializable
@SerialName("query")
@ApolloExperimental
class Query(override val typeName: String) : IrOperationType
@Serializable
@SerialName("mutation")
@ApolloExperimental
class Mutation(override val typeName: String) : IrOperationType
@Serializable
@SerialName("subscription")
@ApolloExperimental
class Subscription(override val typeName: String) : IrOperationType
}

Expand All @@ -193,7 +202,8 @@ internal sealed interface IrOperationType {
* TODO: maybe merge this with [IrProperty]
*/
@Serializable
internal data class IrFieldInfo(
@ApolloExperimental
data class IrFieldInfo(
/**
* The responseName of this field (or synthetic name)
*/
Expand Down Expand Up @@ -237,7 +247,8 @@ internal data class IrFieldInfo(
)

@Serializable
internal sealed class IrAccessor {
@ApolloExperimental
sealed class IrAccessor {
abstract val returnedModelId: String
}

Expand All @@ -261,7 +272,8 @@ internal data class IrSubtypeAccessor(
* Monomorphic fields will always be represented by a class while polymorphic fields will involve interfaces
*/
@Serializable
internal data class IrModel(
@ApolloExperimental
data class IrModel(
val modelName: String,
/**
* The path to this field. See [IrModelType] for more details
Expand Down Expand Up @@ -297,7 +309,8 @@ internal data class IrModel(
* @param requiresBuffering true if this property contains synthetic properties and needs to be buffered
*/
@Serializable
internal data class IrProperty(
@ApolloExperimental
data class IrProperty(
val info: IrFieldInfo,
val override: Boolean,
@Serializable(with = BooleanExpressionSerializer::class)
Expand All @@ -320,13 +333,15 @@ internal data class IrProperty(
}

@Serializable
internal data class IrModelGroup(
@ApolloExperimental
data class IrModelGroup(
val baseModelId: String,
val models: List<IrModel>,
)

@Serializable
internal data class IrVariable(
@ApolloExperimental
data class IrVariable(
val name: String,
val type: IrType,
/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ internal class IrOperationsBuilder(
}
}

return DefaultIrOperations(
return IrOperations(
operations = operations,
fragments = fragments,
usedFields = usedFields,
Expand Down Expand Up @@ -296,10 +296,6 @@ internal class IrOperationsBuilder(
operationName = name!!
)

val responseBasedModelGroup = when (codegenModels) {
MODELS_RESPONSE_BASED -> dataModelGroup
else -> null
}
// Add the root type to use from the selections
usedFields.putType(typeDefinition.name)
return IrOperation(
Expand All @@ -313,7 +309,6 @@ internal class IrOperationsBuilder(
normalizedFilePath = operationNameToNormalizedPath.get(name!!) ?: "",
dataProperty = dataProperty,
dataModelGroup = dataModelGroup,
responseBasedDataModelGroup = responseBasedModelGroup,
ignoreErrors = directives.any {
schema.originalDirectiveName(it.name) == Schema.IGNORE_ERRORS
}
Expand Down

0 comments on commit a76eed5

Please sign in to comment.