Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Codegen] Add CompiledArgumentDefinition #5797

Merged
merged 18 commits into from
Apr 15, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
4 changes: 2 additions & 2 deletions .idea/codeStyles/Project.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 17 additions & 3 deletions libraries/apollo-api/api/apollo-api.api
Original file line number Diff line number Diff line change
Expand Up @@ -266,19 +266,33 @@ public abstract interface class com/apollographql/apollo3/api/BuilderScope {
}

public final class com/apollographql/apollo3/api/CompiledArgument {
public synthetic fun <init> (Ljava/lang/String;Lcom/apollographql/apollo3/api/Optional;ZZLkotlin/jvm/internal/DefaultConstructorMarker;)V
public synthetic fun <init> (Lcom/apollographql/apollo3/api/CompiledArgumentDefinition;Lcom/apollographql/apollo3/api/Optional;Lkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getDefinition ()Lcom/apollographql/apollo3/api/CompiledArgumentDefinition;
public final fun getName ()Ljava/lang/String;
public final fun getValue ()Lcom/apollographql/apollo3/api/Optional;
public final fun isKey ()Z
}

public final class com/apollographql/apollo3/api/CompiledArgument$Builder {
public fun <init> (Ljava/lang/String;)V
public fun <init> (Lcom/apollographql/apollo3/api/CompiledArgumentDefinition;)V
public final fun build ()Lcom/apollographql/apollo3/api/CompiledArgument;
public final fun isKey (Z)Lcom/apollographql/apollo3/api/CompiledArgument$Builder;
martinbonnin marked this conversation as resolved.
Show resolved Hide resolved
public final fun value (Ljava/lang/Object;)Lcom/apollographql/apollo3/api/CompiledArgument$Builder;
}

public final class com/apollographql/apollo3/api/CompiledArgumentDefinition {
public synthetic fun <init> (Ljava/lang/String;ZZLkotlin/jvm/internal/DefaultConstructorMarker;)V
public final fun getName ()Ljava/lang/String;
public final fun isKey ()Z
public final fun newBuilder ()Lcom/apollographql/apollo3/api/CompiledArgumentDefinition$Builder;
}

public final class com/apollographql/apollo3/api/CompiledArgumentDefinition$Builder {
public fun <init> (Lcom/apollographql/apollo3/api/CompiledArgumentDefinition;)V
public fun <init> (Ljava/lang/String;)V
public final fun build ()Lcom/apollographql/apollo3/api/CompiledArgumentDefinition;
public final fun isKey (Z)Lcom/apollographql/apollo3/api/CompiledArgumentDefinition$Builder;
}

public final class com/apollographql/apollo3/api/CompiledCondition {
public fun <init> (Ljava/lang/String;Z)V
public final fun component1 ()Ljava/lang/String;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class CompiledField internal constructor(
* value with variables substituted for their values.
*/
@Deprecated("This function does not distinguish between null and absent arguments. Use argumentValue instead", ReplaceWith("argumentValue(name = name, variables = variables)"))
@ApolloDeprecatedSince(ApolloDeprecatedSince.Version.v4_0_0)
@ApolloDeprecatedSince(v4_0_0)
fun resolveArgument(
name: String,
variables: Executable.Variables,
Expand All @@ -56,7 +56,7 @@ class CompiledField internal constructor(
name: String,
variables: Executable.Variables,
): Optional<ApolloJsonElement> {
val argument = arguments.firstOrNull { it.name == name }
val argument = arguments.firstOrNull { it.definition.name == name }
if (argument == null) {
// no such argument
return Optional.Absent
Expand Down Expand Up @@ -86,12 +86,12 @@ class CompiledField internal constructor(
* Absent arguments are not returned
*/
@ApolloExperimental
fun argumentValues(variables: Executable.Variables, filter: (CompiledArgument) -> Boolean= {true}): Map<String, ApolloJsonElement> {
fun argumentValues(variables: Executable.Variables, filter: (CompiledArgument) -> Boolean = { true }): Map<String, ApolloJsonElement> {
val arguments = arguments.filter(filter).filter { it.value is Optional.Present<*> }
if (arguments.isEmpty()) {
return emptyMap()
}
val map = arguments.associate { it.name to it.value.getOrThrow() }
val map = arguments.associate { it.definition.name to it.value.getOrThrow() }

@Suppress("UNCHECKED_CAST")
return resolveVariables(map, variables) as Map<String, ApolloJsonElement>
Expand Down Expand Up @@ -148,7 +148,7 @@ class CompiledField internal constructor(
* CacheKey1: `users({"ids": 42})`
*/
fun nameWithArguments(variables: Executable.Variables): String {
val arguments = argumentValues(variables) { !it.isPagination }
val arguments = argumentValues(variables) { !it.definition.isPagination }
if (arguments.isEmpty()) {
return name
}
Expand Down Expand Up @@ -412,50 +412,78 @@ class CompiledVariable(val name: String)
*/
typealias CompiledValue = Any?

class CompiledArgument private constructor(
class CompiledArgumentDefinition private constructor(
val name: String,
/**
* The compile-time value of that argument.
*
* Can be the defaultValue if no argument is defined in the operation.
* Can contain variables.
* Can be [Optional.Absent] if no value is passed
*/
val value: Optional<CompiledValue>,
val isKey: Boolean = false,
val isKey: Boolean,

@ApolloExperimental
val isPagination: Boolean = false,
val isPagination: Boolean,
) {
fun newBuilder(): Builder = Builder(this)

class Builder(
private val name: String,
) {
private var value: Optional<CompiledValue> = Optional.absent()
constructor(argumentDefinition: CompiledArgumentDefinition) : this(argumentDefinition.name) {
this.isKey = argumentDefinition.isKey
this.isPagination = argumentDefinition.isPagination
}

private var isKey: Boolean = false
private var isPagination: Boolean = false

fun isKey(isKey: Boolean) = apply {
this.isKey = isKey
}

fun value(value: CompiledValue) = apply {
this.value = Optional.present(value)
}

@ApolloExperimental
fun isPagination(isPagination: Boolean) = apply {
this.isPagination = isPagination
}


fun build(): CompiledArgument = CompiledArgument(
fun build(): CompiledArgumentDefinition = CompiledArgumentDefinition(
name = name,
value = value,
isKey = isKey,
isPagination = isPagination,
)
}
}

class CompiledArgument private constructor(
val definition: CompiledArgumentDefinition,
/**
* The compile-time value of that argument.
*
* Can be the defaultValue if no argument is defined in the operation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't we go for "no need for the schema defaultValue"?

* Can contain variables.
* Can be [Optional.Absent] if no value is passed
*/
val value: Optional<CompiledValue>,
) {
@Deprecated("Use definition.name instead", ReplaceWith("definition.name"))
@ApolloDeprecatedSince(v4_0_0)
val name get() = definition.name

@Deprecated("Use definition.isKey instead", ReplaceWith("definition.isKey"))
@ApolloDeprecatedSince(v4_0_0)
val isKey get() = definition.isKey

class Builder(
private val definition: CompiledArgumentDefinition,
) {
private var value: Optional<CompiledValue> = Optional.absent()

fun value(value: CompiledValue) = apply {
this.value = Optional.present(value)
}

fun build(): CompiledArgument = CompiledArgument(
definition = definition,
value = value,
)
}
}

/**
* Resolve all variables that may be contained inside `value`
*
Expand Down
37 changes: 34 additions & 3 deletions libraries/apollo-compiler/api/apollo-compiler.api
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ public final class com/apollographql/apollo3/compiler/ApolloCompiler {
public final fun buildExecutableSchemaSources (Lcom/apollographql/apollo3/compiler/CodegenSchema;Lcom/apollographql/apollo3/compiler/CodegenMetadata;Ljava/util/List;Ljava/lang/String;Ljava/lang/String;)Lcom/apollographql/apollo3/compiler/codegen/SourceOutput;
public final fun buildIrOperations (Lcom/apollographql/apollo3/compiler/CodegenSchema;Ljava/util/List;Ljava/util/List;Ljava/util/List;Lcom/apollographql/apollo3/compiler/IrOptions;Lcom/apollographql/apollo3/compiler/ApolloCompiler$Logger;)Lcom/apollographql/apollo3/compiler/ir/IrOperations;
public final fun buildSchemaAndOperationsSources (Ljava/util/List;Ljava/util/List;Lcom/apollographql/apollo3/compiler/CodegenSchemaOptions;Lcom/apollographql/apollo3/compiler/IrOptions;Lcom/apollographql/apollo3/compiler/CodegenOptions;Lcom/apollographql/apollo3/compiler/LayoutFactory;Lcom/apollographql/apollo3/compiler/OperationOutputGenerator;Lcom/apollographql/apollo3/compiler/Transform;Lcom/apollographql/apollo3/compiler/Transform;Lcom/apollographql/apollo3/compiler/Transform;Lcom/apollographql/apollo3/compiler/ApolloCompiler$Logger;Ljava/io/File;)Lcom/apollographql/apollo3/compiler/codegen/SourceOutput;
public final fun buildSchemaAndOperationsSourcesFromIr (Lcom/apollographql/apollo3/compiler/CodegenSchema;Lcom/apollographql/apollo3/compiler/ir/IrOperations;Ljava/util/Map;Ljava/util/List;Lcom/apollographql/apollo3/compiler/CodegenOptions;Lcom/apollographql/apollo3/compiler/codegen/SchemaAndOperationsLayout;Lcom/apollographql/apollo3/compiler/OperationOutputGenerator;Lcom/apollographql/apollo3/compiler/Transform;Lcom/apollographql/apollo3/compiler/Transform;Lcom/apollographql/apollo3/compiler/Transform;Ljava/io/File;)Lcom/apollographql/apollo3/compiler/codegen/SourceOutput;
public final fun buildSchemaSources (Lcom/apollographql/apollo3/compiler/CodegenSchema;Ljava/util/Map;Lcom/apollographql/apollo3/compiler/CodegenOptions;Lcom/apollographql/apollo3/compiler/codegen/SchemaLayout;Lcom/apollographql/apollo3/compiler/Transform;Lcom/apollographql/apollo3/compiler/Transform;)Lcom/apollographql/apollo3/compiler/codegen/SourceOutput;
public final fun buildSchemaAndOperationsSourcesFromIr (Lcom/apollographql/apollo3/compiler/CodegenSchema;Lcom/apollographql/apollo3/compiler/ir/IrOperations;Lcom/apollographql/apollo3/compiler/UsedCoordinates;Ljava/util/List;Lcom/apollographql/apollo3/compiler/CodegenOptions;Lcom/apollographql/apollo3/compiler/codegen/SchemaAndOperationsLayout;Lcom/apollographql/apollo3/compiler/OperationOutputGenerator;Lcom/apollographql/apollo3/compiler/Transform;Lcom/apollographql/apollo3/compiler/Transform;Lcom/apollographql/apollo3/compiler/Transform;Ljava/io/File;)Lcom/apollographql/apollo3/compiler/codegen/SourceOutput;
public final fun buildSchemaSources (Lcom/apollographql/apollo3/compiler/CodegenSchema;Lcom/apollographql/apollo3/compiler/UsedCoordinates;Lcom/apollographql/apollo3/compiler/CodegenOptions;Lcom/apollographql/apollo3/compiler/codegen/SchemaLayout;Lcom/apollographql/apollo3/compiler/Transform;Lcom/apollographql/apollo3/compiler/Transform;)Lcom/apollographql/apollo3/compiler/codegen/SourceOutput;
}

public abstract interface class com/apollographql/apollo3/compiler/ApolloCompiler$Logger {
Expand Down Expand Up @@ -422,8 +422,39 @@ public abstract interface class com/apollographql/apollo3/compiler/Transform {
public abstract fun transform (Ljava/lang/Object;)Ljava/lang/Object;
}

public final class com/apollographql/apollo3/compiler/UsedCoordinates {
public static final field Companion Lcom/apollographql/apollo3/compiler/UsedCoordinates$Companion;
public fun <init> ()V
public final fun asMap ()Ljava/util/Map;
public final fun getFields (Ljava/lang/String;)Ljava/util/Map;
public final fun getTypes ()Ljava/util/Set;
public final fun hasArgument (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z
public final fun hasField (Ljava/lang/String;Ljava/lang/String;)Z
public final fun mergeWith (Lcom/apollographql/apollo3/compiler/UsedCoordinates;)Lcom/apollographql/apollo3/compiler/UsedCoordinates;
public final fun putAllFields (Ljava/lang/String;Ljava/util/Map;)V
public final fun putArgument (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
public final fun putField (Ljava/lang/String;Ljava/lang/String;)V
public final fun putType (Ljava/lang/String;)V
}

public final class com/apollographql/apollo3/compiler/UsedCoordinates$$serializer : kotlinx/serialization/internal/GeneratedSerializer {
public static final field INSTANCE Lcom/apollographql/apollo3/compiler/UsedCoordinates$$serializer;
public fun childSerializers ()[Lkotlinx/serialization/KSerializer;
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lcom/apollographql/apollo3/compiler/UsedCoordinates;
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lcom/apollographql/apollo3/compiler/UsedCoordinates;)V
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
public fun typeParametersSerializers ()[Lkotlinx/serialization/KSerializer;
}

public final class com/apollographql/apollo3/compiler/UsedCoordinates$Companion {
public final fun serializer ()Lkotlinx/serialization/KSerializer;
}

public final class com/apollographql/apollo3/compiler/UsedCoordinatesKt {
public static final fun mergeWith (Ljava/util/Map;Ljava/util/Map;)Ljava/util/Map;
public static final fun toUsedCoordinates (Ljava/util/Map;)Lcom/apollographql/apollo3/compiler/UsedCoordinates;
public static final fun toUsedCoordinates (Ljava/util/Set;)Lcom/apollographql/apollo3/compiler/UsedCoordinates;
}

public final class com/apollographql/apollo3/compiler/VersionKt {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -287,12 +287,12 @@ object ApolloCompiler {
): IrSchema {

@Suppress("NAME_SHADOWING")
val usedCoordinates = usedCoordinates?.mergeWith((codegenSchema.scalarMapping.keys + setOf("Int", "Float", "String", "ID", "Boolean")).associateWith { emptySet() })
?: codegenSchema.schema.typeDefinitions.keys.associateWith { emptySet() }
val usedCoordinates = usedCoordinates?.mergeWith((codegenSchema.scalarMapping.keys + setOf("Int", "Float", "String", "ID", "Boolean")).toUsedCoordinates() )
?: codegenSchema.schema.typeDefinitions.keys.toUsedCoordinates()

return IrSchemaBuilder.build(
schema = codegenSchema.schema,
usedFields = usedCoordinates,
usedCoordinates = usedCoordinates,
alreadyVisitedTypes = emptySet(),
)
}
Expand Down Expand Up @@ -418,7 +418,7 @@ object ApolloCompiler {
if (upstreamCodegenMetadata.isEmpty()) {
sourceOutput = sourceOutput plus buildSchemaSources(
codegenSchema = codegenSchema,
usedCoordinates = downstreamUsedCoordinates?.mergeWith(irOperations.usedFields),
usedCoordinates = downstreamUsedCoordinates?.mergeWith(irOperations.usedCoordinates),
codegenOptions = codegenOptions,
schemaLayout = layout,
javaOutputTransform = javaOutputTransform,
Expand Down Expand Up @@ -486,7 +486,7 @@ object ApolloCompiler {
val sourceOutput = buildSchemaAndOperationsSourcesFromIr(
codegenSchema = codegenSchema,
irOperations = irOperations,
downstreamUsedCoordinates = emptyMap(),
downstreamUsedCoordinates = UsedCoordinates(),
upstreamCodegenMetadata = emptyList(),
codegenOptions = codegenOptions,
layout = layoutFactory?.create(codegenSchema),
Expand Down Expand Up @@ -584,4 +584,4 @@ internal fun <T> T.maybeTransform(transform: Transform<T>?) = transform?.transfo

interface LayoutFactory {
fun create(codegenSchema: CodegenSchema): SchemaAndOperationsLayout?
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,113 @@
package com.apollographql.apollo3.compiler

typealias UsedCoordinates = Map<String, Set<String>>
import kotlinx.serialization.Serializable

fun UsedCoordinates.mergeWith(other: UsedCoordinates): UsedCoordinates {
return (entries + other.entries).groupBy { it.key }.mapValues {
it.value.map { it.value }.fold(emptySet()) { acc, set ->
(acc + set).toSet()
@Serializable
class UsedCoordinates {
private val typeToFieldsToArguments = mutableMapOf<String, MutableMap<String, MutableSet<String>>>()

fun putType(type: String) {
val usedFields = typeToFieldsToArguments[type]
if (usedFields == null) {
typeToFieldsToArguments[type] = mutableMapOf()
}
}

fun putField(type: String, field: String) {
val usedFields = typeToFieldsToArguments[type]
if (usedFields == null) {
typeToFieldsToArguments[type] = mutableMapOf()
}
if (typeToFieldsToArguments[type]!![field] == null) {
typeToFieldsToArguments[type]!![field] = mutableSetOf()
}
}

fun putAllFields(type: String, fields: Map<String, Set<String>>) {
val usedFields = typeToFieldsToArguments[type]
if (usedFields == null) {
typeToFieldsToArguments[type] = mutableMapOf()
}
for ((field, arguments) in fields) {
if (typeToFieldsToArguments[type]!![field] == null) {
typeToFieldsToArguments[type]!![field] = mutableSetOf()
}
typeToFieldsToArguments[type]!![field]!!.addAll(arguments)
}
}

fun putArgument(type: String, field: String, argument: String) {
val usedFields = typeToFieldsToArguments[type]
if (usedFields == null) {
typeToFieldsToArguments[type] = mutableMapOf()
}
if (typeToFieldsToArguments[type]!![field] == null) {
typeToFieldsToArguments[type]!![field] = mutableSetOf()
}
typeToFieldsToArguments[type]!![field]!!.add(argument)
}

fun getTypes(): Set<String> {
return typeToFieldsToArguments.keys
}

fun getFields(type: String): Map<String, Set<String>> {
return typeToFieldsToArguments[type].orEmpty()
}

fun hasField(type: String, field: String): Boolean {
return typeToFieldsToArguments[type]?.containsKey(field) ?: false
}

fun hasArgument(type: String, field: String, argument: String): Boolean {
return typeToFieldsToArguments[type]?.get(field)?.contains(argument) ?: false
}

fun mergeWith(other: UsedCoordinates): UsedCoordinates {
val newUsedCoordinates = UsedCoordinates()
(typeToFieldsToArguments.entries + other.typeToFieldsToArguments.entries)
.groupBy { it.key }
.mapValues { (_, v) -> v.map { it.value } }
.forEach { (type, fieldsList) ->
val fields = fieldsList.reduce { acc, fields ->
val newFields = mutableMapOf<String, MutableSet<String>>()
(acc.entries + fields.entries)
.groupBy { it.key }
.mapValues { (_, v) -> v.map { it.value } }
.forEach { (field, argumentsList) ->
val arguments = argumentsList.reduce { acc, arguments ->
(acc + arguments).toMutableSet()
}
newFields[field] = arguments
}
newFields
}
newUsedCoordinates.typeToFieldsToArguments[type] = fields
}
return newUsedCoordinates
martinbonnin marked this conversation as resolved.
Show resolved Hide resolved
}

fun asMap(): Map<String, Map<String, Set<String>>> = typeToFieldsToArguments
}

fun Map<String, Map<String, Set<String>>>.toUsedCoordinates(): UsedCoordinates {
val usedCoordinates = UsedCoordinates()
for ((type, fields) in this) {
usedCoordinates.putType(type)
for ((field, arguments) in fields) {
usedCoordinates.putField(type, field)
for (argument in arguments) {
usedCoordinates.putArgument(type, field, argument)
}
}
}
return usedCoordinates
}

fun Set<String>.toUsedCoordinates(): UsedCoordinates {
val usedCoordinates = UsedCoordinates()
for (type in this) {
usedCoordinates.putType(type)
}
return usedCoordinates
}
Loading
Loading