Skip to content

Commit 5e9864f

Browse files
authored
[plugin] better error messages when generating clients (#1182)
* [plugin] better error messages when generating clients Adds operation name to all thrown exceptions so it is easier to find the invalid file. Resolves: #1173 * separate invalid polymorphic selection set exceptions * fix unit tests
1 parent 2ba519b commit 5e9864f

17 files changed

+40
-34
lines changed

plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/GraphQLClientGenerator.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,24 +90,24 @@ class GraphQLClientGenerator(
9090

9191
val operationDefinitions = queryDocument.definitions.filterIsInstance(OperationDefinition::class.java)
9292
if (operationDefinitions.size > 1) {
93-
throw MultipleOperationsInFileException
93+
throw MultipleOperationsInFileException(queryFile)
9494
}
9595

9696
val fileSpecs = mutableListOf<FileSpec>()
9797
val operationFileSpec = FileSpec.builder(packageName = config.packageName, fileName = queryFile.nameWithoutExtension.capitalize())
9898
operationDefinitions.forEach { operationDefinition ->
99-
val operationTypeName = operationDefinition.name?.capitalize() ?: queryFile.nameWithoutExtension.capitalize()
99+
val capitalizedOperationName = operationDefinition.name?.capitalize() ?: queryFile.nameWithoutExtension.capitalize()
100100
val context = GraphQLClientGeneratorContext(
101101
packageName = config.packageName,
102102
graphQLSchema = graphQLSchema,
103-
rootType = operationTypeName,
103+
operationName = capitalizedOperationName,
104104
queryDocument = queryDocument,
105105
allowDeprecated = config.allowDeprecated,
106106
customScalarMap = config.customScalarMap,
107107
serializer = config.serializer,
108108
useOptionalInputWrapper = config.useOptionalInputWrapper
109109
)
110-
val queryConstName = operationTypeName.toUpperUnderscore()
110+
val queryConstName = capitalizedOperationName.toUpperUnderscore()
111111
val queryConstProp = PropertySpec.builder(queryConstName, STRING)
112112
.addModifiers(KModifier.CONST)
113113
.initializer("%S", queryConst)
@@ -116,12 +116,12 @@ class GraphQLClientGenerator(
116116

117117
val rootType = findRootType(operationDefinition)
118118
val graphQLResponseTypeSpec = generateGraphQLObjectTypeSpec(context, rootType, operationDefinition.selectionSet, "Result")
119-
val kotlinResultTypeName = ClassName(context.packageName, "${context.rootType}.${graphQLResponseTypeSpec.name}")
119+
val kotlinResultTypeName = ClassName(context.packageName, "${context.operationName}.${graphQLResponseTypeSpec.name}")
120120

121121
val queryProperty = PropertySpec.builder("query", STRING, KModifier.OVERRIDE)
122122
.initializer("%N", queryConstProp)
123123
.build()
124-
val operationTypeSpec = TypeSpec.classBuilder(operationTypeName)
124+
val operationTypeSpec = TypeSpec.classBuilder(capitalizedOperationName)
125125
.addSuperinterface(ClassName(CORE_TYPES_PACKAGE, "GraphQLClientRequest").parameterizedBy(kotlinResultTypeName))
126126
.addProperty(queryProperty)
127127

@@ -139,7 +139,7 @@ class GraphQLClientGenerator(
139139
if (variableType != null) {
140140
operationTypeSpec.addType(variableType)
141141

142-
val variablesClassName = ClassName(config.packageName, "$operationTypeName.Variables")
142+
val variablesClassName = ClassName(config.packageName, "$capitalizedOperationName.Variables")
143143
val variablesProperty = PropertySpec.builder("variables", variablesClassName, KModifier.OVERRIDE)
144144
.initializer("variables")
145145
.build()

plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/GraphQLClientGeneratorContext.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,17 +30,18 @@ import graphql.schema.idl.TypeDefinitionRegistry
3030
data class GraphQLClientGeneratorContext(
3131
val packageName: String,
3232
val graphQLSchema: TypeDefinitionRegistry,
33-
val rootType: String,
33+
val operationName: String,
3434
val queryDocument: Document,
3535
val allowDeprecated: Boolean = false,
3636
val customScalarMap: Map<String, GraphQLScalar> = mapOf(),
3737
val serializer: GraphQLSerializer = GraphQLSerializer.JACKSON,
3838
val useOptionalInputWrapper: Boolean = false
3939
) {
40+
// per operation caches
4041
val typeSpecs: MutableMap<ClassName, TypeSpec> = mutableMapOf()
4142
val polymorphicTypes: MutableMap<ClassName, MutableList<ClassName>> = mutableMapOf()
4243

43-
// shared types
44+
// shared type caches
4445
val enumClassToTypeSpecs: MutableMap<ClassName, TypeSpec> = mutableMapOf()
4546
val inputClassToTypeSpecs: MutableMap<ClassName, TypeSpec> = mutableMapOf()
4647
val scalarsClassToTypeSpec: MutableMap<ClassName, MutableList<TypeSpec>> = mutableMapOf()

plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/exceptions/DeprecatedFieldsSelectedException.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ package com.expediagroup.graphql.plugin.client.generator.exceptions
1919
/**
2020
* Exception thrown when query specifies deprecated fields but client configuration does not allow it.
2121
*/
22-
internal class DeprecatedFieldsSelectedException(field: String, type: String) :
23-
RuntimeException("Operation specifies deprecated field - $field in $type. Update your operation or update your configuration to allow usage of deprecated fields")
22+
internal class DeprecatedFieldsSelectedException(operationName: String, field: String, type: String) :
23+
RuntimeException("Operation $operationName specifies deprecated field - $field in $type. Update your operation or update your configuration to allow usage of deprecated fields")

plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/exceptions/InvalidFragmentException.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ package com.expediagroup.graphql.plugin.client.generator.exceptions
1919
/**
2020
* Exception thrown when query file specifies invalid fragment.
2121
*/
22-
internal class InvalidFragmentException(fragmentName: String, targetType: String) :
23-
RuntimeException("Invalid fragment, $fragmentName fragment not found or does not reference valid $targetType type")
22+
internal class InvalidFragmentException(operationName: String, fragmentName: String, targetType: String) :
23+
RuntimeException("Operation $operationName specifies invalid fragment, $fragmentName fragment not found or does not reference valid $targetType type")

plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/exceptions/InvalidPolymorphicQueryException.kt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.expediagroup.graphql.plugin.client.generator.exceptions
1818

1919
/**
20-
* Exception thrown when query contains invalid polymorphic selection sets.
20+
* Exception thrown when query does not select all implementations of polymorphic type.
2121
*/
22-
internal class InvalidPolymorphicQueryException(message: String) : RuntimeException(message)
22+
internal class InvalidPolymorphicQueryException(operationName: String, interfaceName: String, missingImplementations: List<String>) :
23+
RuntimeException("Operation $operationName does not specify all polymorphic implementations - $interfaceName field selection is missing $missingImplementations")

plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/exceptions/InvalidSelectionSetException.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ package com.expediagroup.graphql.plugin.client.generator.exceptions
1919
/**
2020
* Exception thrown when specified query file contains invalid selection set.
2121
*/
22-
internal class InvalidSelectionSetException(typeDefinitionName: String, typeName: String) :
23-
RuntimeException("Invalid selection set for $typeName - cannot select empty $typeDefinitionName")
22+
internal class InvalidSelectionSetException(operationName: String, typeDefinitionName: String, typeName: String) :
23+
RuntimeException("Operation $operationName specifies invalid selection set for $typeName - cannot select empty $typeDefinitionName")
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
package com.expediagroup.graphql.plugin.client.generator.exceptions
1818

1919
/**
20-
* Exception thrown when specified query file references unknown fields.
20+
* Exception thrown when polymorphic query does not specify __typename information.
2121
*/
22-
internal class UnknownFieldSelectedException(fieldName: String, typeDefinitionName: String) :
23-
RuntimeException("Invalid selection set for $typeDefinitionName - unknown $fieldName field selected")
22+
internal class MissingTypeNameException(operationName: String, interfaceName: String, implementationName: String) :
23+
RuntimeException("Operation $operationName specifies invalid polymorphic selection set - $implementationName implementation of $interfaceName is missing __typename field in its selection set")

plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/exceptions/MultipleOperationsInFileException.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616

1717
package com.expediagroup.graphql.plugin.client.generator.exceptions
1818

19+
import java.io.File
20+
1921
/**
2022
* Exception thrown when attempting to generate a client from a query file containing multiple operations.
2123
*/
22-
internal object MultipleOperationsInFileException : RuntimeException("GraphQL client does not support query files with multiple operations")
24+
internal class MultipleOperationsInFileException(queryFile: File) :
25+
RuntimeException("GraphQL client does not support query files with multiple operations, ${queryFile.name} contains multiple operations")

plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/extensions/DocumentExtensions.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,4 @@ import graphql.language.FragmentDefinition
2424
internal fun Document.findFragmentDefinition(context: GraphQLClientGeneratorContext, targetFragment: String, targetType: String): FragmentDefinition =
2525
this.getDefinitionsOfType(FragmentDefinition::class.java)
2626
.find { it.name == targetFragment && context.graphQLSchema.getType(it.typeCondition.name).isPresent }
27-
?: throw InvalidFragmentException(targetFragment, targetType)
27+
?: throw InvalidFragmentException(context.operationName, targetFragment, targetType)

plugins/client/graphql-kotlin-client-generator/src/main/kotlin/com/expediagroup/graphql/plugin/client/generator/types/generateGraphQLInterfaceTypeSpec.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ internal fun generateGraphQLInterfaceTypeSpec(
3535
interfaceNameOverride: String? = null
3636
): TypeSpec {
3737
if (selectionSet == null || selectionSet.selections.isEmpty()) {
38-
throw InvalidSelectionSetException(interfaceDefinition.name, "interface")
38+
throw InvalidSelectionSetException(context.operationName, interfaceDefinition.name, "interface")
3939
}
4040

4141
val interfaceName = interfaceNameOverride ?: interfaceDefinition.name

0 commit comments

Comments
 (0)