Skip to content

Commit 9822b20

Browse files
authored
Add allowAddingDirectivesToExistingFieldDefinitions (#6470)
* Add allowAddingDirectivesToExistingFieldDefinitions * update apiDump * fix builD
1 parent cf061aa commit 9822b20

File tree

7 files changed

+43
-24
lines changed

7 files changed

+43
-24
lines changed

libraries/apollo-ast/api/apollo-ast.api

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -953,7 +953,8 @@ public final class com/apollographql/apollo/ast/IssueKt {
953953

954954
public final class com/apollographql/apollo/ast/MergeOptions {
955955
public static final field Companion Lcom/apollographql/apollo/ast/MergeOptions$Companion;
956-
public fun <init> (Z)V
956+
public fun <init> (ZZ)V
957+
public final fun getAllowAddingDirectivesToExistingFieldDefinitions ()Z
957958
public final fun getAllowFieldNullabilityModification ()Z
958959
}
959960

libraries/apollo-ast/api/apollo-ast.klib.api

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1093,8 +1093,10 @@ final class com.apollographql.apollo.ast/InvalidDeferLabel : com.apollographql.a
10931093
}
10941094

10951095
final class com.apollographql.apollo.ast/MergeOptions { // com.apollographql.apollo.ast/MergeOptions|null[0]
1096-
constructor <init>(kotlin/Boolean) // com.apollographql.apollo.ast/MergeOptions.<init>|<init>(kotlin.Boolean){}[0]
1096+
constructor <init>(kotlin/Boolean, kotlin/Boolean) // com.apollographql.apollo.ast/MergeOptions.<init>|<init>(kotlin.Boolean;kotlin.Boolean){}[0]
10971097

1098+
final val allowAddingDirectivesToExistingFieldDefinitions // com.apollographql.apollo.ast/MergeOptions.allowAddingDirectivesToExistingFieldDefinitions|{}allowAddingDirectivesToExistingFieldDefinitions[0]
1099+
final fun <get-allowAddingDirectivesToExistingFieldDefinitions>(): kotlin/Boolean // com.apollographql.apollo.ast/MergeOptions.allowAddingDirectivesToExistingFieldDefinitions.<get-allowAddingDirectivesToExistingFieldDefinitions>|<get-allowAddingDirectivesToExistingFieldDefinitions>(){}[0]
10981100
final val allowFieldNullabilityModification // com.apollographql.apollo.ast/MergeOptions.allowFieldNullabilityModification|{}allowFieldNullabilityModification[0]
10991101
final fun <get-allowFieldNullabilityModification>(): kotlin/Boolean // com.apollographql.apollo.ast/MergeOptions.allowFieldNullabilityModification.<get-allowFieldNullabilityModification>|<get-allowFieldNullabilityModification>(){}[0]
11001102

libraries/apollo-ast/src/commonMain/kotlin/com/apollographql/apollo/ast/gqldocument.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import com.apollographql.apollo.annotations.ApolloInternal
66
import com.apollographql.apollo.ast.internal.ExtensionsMerger
77
import com.apollographql.apollo.ast.internal.builtinsDefinitionsStr
88
import com.apollographql.apollo.ast.internal.compilerOptions_0_0
9-
import com.apollographql.apollo.ast.internal.disableErrorPropagationStr
109
import com.apollographql.apollo.ast.internal.kotlinLabsDefinitions_0_3
1110
import com.apollographql.apollo.ast.internal.kotlinLabsDefinitions_0_4
1211
import com.apollographql.apollo.ast.internal.kotlinLabsDefinitions_0_5
@@ -64,10 +63,11 @@ fun GQLDocument.toSchema(): Schema = validateAsSchema().getOrThrow()
6463

6564
@ApolloExperimental
6665
class MergeOptions(
66+
val allowAddingDirectivesToExistingFieldDefinitions: Boolean,
6767
val allowFieldNullabilityModification: Boolean
6868
) {
6969
companion object {
70-
val Default: MergeOptions = MergeOptions(false)
70+
val Default: MergeOptions = MergeOptions(false, false)
7171
}
7272
}
7373

libraries/apollo-ast/src/commonMain/kotlin/com/apollographql/apollo/ast/internal/ExtensionsMerger.kt

Lines changed: 22 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -316,28 +316,40 @@ private fun ExtensionsMerger.mergeFields(
316316
// field doesn't exist, add it
317317
result.add(newFieldDefinition)
318318
} else {
319-
val existingFieldDefinition = result[index]
320-
if (!mergeOptions.allowFieldNullabilityModification) {
319+
if (!mergeOptions.allowFieldNullabilityModification && !mergeOptions.allowAddingDirectivesToExistingFieldDefinitions) {
321320
issues.add(OtherValidationIssue("There is already a field definition named `${newFieldDefinition.name}` for this type", newFieldDefinition.sourceLocation))
322321
return@forEach
323322
}
324323

324+
val existingFieldDefinition = result[index]
325325
if (!areEqual(newFieldDefinition.arguments, existingFieldDefinition.arguments)) {
326326
issues.add(OtherValidationIssue("Cannot merge field definition `${newFieldDefinition.name}`: its arguments do not match the arguments of the original field definition", newFieldDefinition.sourceLocation))
327327
return@forEach
328328
}
329329

330-
if (newFieldDefinition.directives.isNotEmpty()) {
331-
issues.add(OtherValidationIssue("Cannot add directives to existing field definition `${newFieldDefinition.name}`", newFieldDefinition.sourceLocation))
332-
return@forEach
330+
if (mergeOptions.allowFieldNullabilityModification) {
331+
if (!newFieldDefinition.type.isCompatibleWith(existingFieldDefinition.type)) {
332+
issues.add(OtherValidationIssue("Cannot merge field definition `${newFieldDefinition.name}`: its type is not compatible with the original type.", newFieldDefinition.sourceLocation))
333+
return@forEach
334+
}
335+
} else {
336+
if (newFieldDefinition.type.toUtf8() != existingFieldDefinition.type.toUtf8()) {
337+
issues.add(OtherValidationIssue("Cannot merge field definition`${newFieldDefinition.name}`: they have different types.", newFieldDefinition.sourceLocation))
338+
return@forEach
339+
}
333340
}
334-
335-
if (!newFieldDefinition.type.isCompatibleWith(existingFieldDefinition.type)) {
336-
issues.add(OtherValidationIssue("Cannot merge field directives`${newFieldDefinition.name}`: its type is not compatible with the original type`", newFieldDefinition.sourceLocation))
337-
return@forEach
341+
if (!mergeOptions.allowAddingDirectivesToExistingFieldDefinitions) {
342+
if (newFieldDefinition.directives.isNotEmpty()) {
343+
issues.add(OtherValidationIssue("Cannot add directives to existing field definition `${newFieldDefinition.name}`", newFieldDefinition.sourceLocation))
344+
return@forEach
345+
}
338346
}
339347

340-
result[index] = existingFieldDefinition.copy(type = newFieldDefinition.type)
348+
/*
349+
* No need to validate repeated directives, this is done later on by schema validation.
350+
*/
351+
val newDirectives = existingFieldDefinition.directives + newFieldDefinition.directives
352+
result[index] = existingFieldDefinition.copy(type = newFieldDefinition.type, directives = newDirectives)
341353
}
342354
}
343355

libraries/apollo-ast/src/commonMain/kotlin/com/apollographql/apollo/ast/internal/SchemaValidationScope.kt

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ internal fun validateSchema(definitions: List<GQLDefinition>, options: SchemaVal
7878
*/
7979
val fullDefinitions = definitions.withBuiltinDefinitions()
8080

81+
/**
82+
* TODO: this should probably be done after merging so that we can handle @link on the schema definition itself.
83+
*/
8184
val linkedSchemas = definitions.filterIsInstance<GQLSchemaExtension>().getLinkedSchemas(issues, options.foreignSchemas).toMutableList()
8285

8386
if (options.addKotlinLabsDefinitions && linkedSchemas.none { it.foreignSchema.name == "kotlin_labs" }) {
@@ -287,15 +290,17 @@ internal fun validateSchema(definitions: List<GQLDefinition>, options: SchemaVal
287290
}
288291

289292
/**
290-
* I'm not 100% clear on the order of validations, here I'm merging the extensions first thing
293+
* I'm not 100% clear on the order of validations, here I'm merging the extensions first thing.
294+
*
295+
* It seems more natural. Two examples:
296+
* - If we were one day to validate that objects implement all interfaces fields for an example, this would have to be
297+
* done post merging (because extensions may add fields to interfaces).
298+
* - Same for validated repeated directives.
291299
*
292-
* Most of the validations that we do later on do not require merging the definitions though.
293-
* If we were one day to validate that objects implement all interfaces fields for an example, this would have to be
294-
* done post merging (because extensions may add fields to interfaces). As it is now, we could probably
295-
* move validation of the directives before merging.
300+
* Moving forward, extensions merging should probably be done first thing as a separate step, before any validation and/or linking of foreign schemas.
296301
*/
297302
val dedupedDefinitions = listOfNotNull(schemaDefinition) + directiveDefinitions.values + typeDefinitions.values
298-
val mergedDefinitions = ExtensionsMerger(dedupedDefinitions + typeSystemExtensions, MergeOptions(true)).merge().getOrThrow()
303+
val mergedDefinitions = ExtensionsMerger(dedupedDefinitions + typeSystemExtensions, MergeOptions(false, true)).merge().getOrThrow()
299304

300305
val mergedScope = DefaultValidationScope(
301306
typeDefinitions = mergedDefinitions.filterIsInstance<GQLTypeDefinition>().associateBy { it.name },

libraries/apollo-ast/src/jvmTest/kotlin/com/apollographql/apollo/graphql/ast/test/TypeExtensionsMergeTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class TypeExtensionsMergeTest {
3030
}
3131
""".trimIndent()
3232

33-
val result = sdl.toGQLDocument().toMergedGQLDocument(MergeOptions(true))
33+
val result = sdl.toGQLDocument().toMergedGQLDocument(MergeOptions(false, true))
3434
.definitions
3535
.single() as GQLObjectTypeDefinition
3636

@@ -60,7 +60,7 @@ class TypeExtensionsMergeTest {
6060
}
6161
""".trimIndent()
6262

63-
val issues = sdl.toGQLDocument().mergeExtensions(MergeOptions(true)).issues
63+
val issues = sdl.toGQLDocument().mergeExtensions(MergeOptions(false, true)).issues
6464

6565
assertEquals(3, issues.size)
6666
assertTrue(issues[0].message.contains("its type is not compatible with the original type"))

libraries/apollo-gradle-plugin-external/src/main/kotlin/com/apollographql/apollo/gradle/internal/utils.kt

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package com.apollographql.apollo.gradle.internal
33
import com.apollographql.apollo.compiler.InputFile
44
import org.gradle.api.artifacts.ProjectDependency
55
import org.gradle.api.file.FileCollection
6-
import java.io.File
76
import java.lang.reflect.InvocationTargetException
87

98

@@ -59,4 +58,4 @@ internal fun Any.reflectiveCall(methodName: String, vararg args: Any?) {
5958
*/
6059
throw e.cause!!
6160
}
62-
}
61+
}

0 commit comments

Comments
 (0)