diff --git a/docs/docs/recommended-usage.md b/docs/docs/recommended-usage.md new file mode 100644 index 0000000..0c4ee79 --- /dev/null +++ b/docs/docs/recommended-usage.md @@ -0,0 +1,118 @@ +--- +sidebar_position: 4 +--- + +# Recommended Usage + +In general, the `resolverClasses` config should be used to generate more performant code. This is especially important +when dealing with expensive operations, such as database queries or network requests. When at least one field has +arguments in a type, we generate an open class with function signatures to be inherited in source code. +However, when fields have no arguments, we generate data classes by default. + +## Example + +The following demonstrates the problem with using generated data classes to implement your resolvers with GraphQL Kotlin. + +Say you want to implement the schema below: + +```graphql +type Query { + resolveMyType(input: String!): MyType +} + +type MyType { + field1: String! + field2: String +} +``` + +### Here is the default behavior. + +Generated Kotlin: + +```kotlin +package com.types.generated + +open class Query { + open fun resolveMyType(input: String): MyType = throw NotImplementedError("Query.resolveMyType must be implemented.") +} + +data class MyType( + val field1: String, + val field2: String? = null +) +``` + +Source code: + +```kotlin +import com.expediagroup.graphql.server.operations.Query +import com.expediagroup.sharedGraphql.generated.Query as QueryInterface +import com.types.generated.MyType + +class MyQuery : Query, QueryInterface() { + override suspend fun resolveMyType(input: String): MyType = + MyType( + field1 = myExpensiveCall1(), + field2 = myExpensiveCall2() + ) +} + +``` + +The resulting source code is at risk of being extremely unperformant. The `MyType` class is a data class, which means +that the `field1` and `field2` properties are both initialized when the `MyType` object is created, and +`myExpensiveCall1()` and `myExpensiveCall2()` will both be called in sequence! Even if I only query for `field1`, not +only will `myExpensiveCall2()` still run, but it will also wait until `myExpensiveCall1()` is totally finished. + +### Instead, use the `resolverClasses` config! + +Codegen config: + +```ts +import { GraphQLKotlinCodegenConfig } from "@expediagroup/graphql-kotlin-codegen"; + +export default { + resolverClasses: [ + { + typeName: "MyType", + }, + ], +} satisfies GraphQLKotlinCodegenConfig; +``` + +Generated Kotlin: + +```kotlin +package com.types.generated + +open class Query { + open fun resolveMyType(input: String): MyType = throw NotImplementedError("Query.resolveMyType must be implemented.") +} + +open class MyType { + open fun field1(): String = throw NotImplementedError("MyType.field1 must be implemented.") + open fun field2(): String? = throw NotImplementedError("MyType.field2 must be implemented.") +} +``` + +Source code: + +```kotlin +import com.types.generated.MyType as MyTypeInterface +import com.expediagroup.graphql.generator.annotations.GraphQLIgnore + +class MyQuery : Query, QueryInterface() { + override suspend fun resolveMyType(input: String): MyType = MyType() +} + +@GraphQLIgnore +class MyType : MyTypeInterface() { + override fun field1(): String = myExpensiveCall1() + override fun field2(): String? = myExpensiveCall2() +} +``` + +This code is much more performant. The `MyType` class is no longer a data class, so the `field1` and `field2` properties +can now be resolved independently of each other. If I query for only `field1`, only `myExpensiveCall1()` will be called, and +if I query for only `field2`, only `myExpensiveCall2()` will be called. diff --git a/src/config.ts b/src/config.ts index 2ee2e23..7bfa079 100644 --- a/src/config.ts +++ b/src/config.ts @@ -103,31 +103,31 @@ export const configSchema = object({ ), ), /** - * Denotes types that should be generated as interfaces with suspense functions. Resolver classes can inherit from these to enforce a type contract. - * @description Two interfaces will be generated: one with suspend functions, and one with `java.util.concurrent.CompletableFuture` functions. - * @example ["MyResolverType1", "MyResolverType2"] + * Denotes types that should be generated as classes. Resolver classes can inherit from these to enforce a type contract. + * @description Type names can be optionally passed with the classMethods config to generate suspend functions or + * `java.util.concurrent.CompletableFuture` functions. + * @example + * [ + * { + * typeName: "MyResolverType", + * }, + * { + * typeName: "MySuspendResolverType", + * classMethods: "SUSPEND", + * }, + * { + * typeName: "MyCompletableFutureResolverType", + * classMethods: "COMPLETABLE_FUTURE", + * } + * ] */ - resolverTypes: optional(array(string())), - /** - * Denotes extra arguments that should be added to functions on resolver classes. - * @example [{ typeNames: ["MyType", "MyType2"], argumentName: "myArgument", argumentValue: "myValue" }] - * @deprecated This will be removed in a future release now that DataFetchingEnvironment is added to functions by default. - */ - extraResolverArguments: optional( + resolverClasses: optional( array( object({ - /** - * The types whose fields to add the argument to. The argument will be added to all fields on each type. If omitted, the argument will be added to all fields on all types. - */ - typeNames: optional(array(string())), - /** - * The name of the argument to add. - */ - argumentName: string(), - /** - * The type of the argument to add. - */ - argumentType: string(), + typeName: string(), + classMethods: optional( + union([literal("SUSPEND"), literal("COMPLETABLE_FUTURE")]), + ), }), ), ), diff --git a/src/definitions/interface.ts b/src/definitions/interface.ts index 3f54f8e..00525ac 100644 --- a/src/definitions/interface.ts +++ b/src/definitions/interface.ts @@ -13,11 +13,11 @@ limitations under the License. import { GraphQLSchema, InterfaceTypeDefinitionNode } from "graphql"; import { buildAnnotations } from "../helpers/build-annotations"; -import { indent } from "@graphql-codegen/visitor-plugin-common"; import { buildTypeMetadata } from "../helpers/build-type-metadata"; import { shouldIncludeTypeDefinition } from "../helpers/should-include-type-definition"; import { buildFieldDefinition } from "../helpers/build-field-definition"; import { CodegenConfigWithDefaults } from "../helpers/build-config-with-defaults"; +import { getDependentInterfaceNames } from "../helpers/dependent-type-utils"; export function buildInterfaceDefinition( node: InterfaceTypeDefinitionNode, @@ -30,25 +30,15 @@ export function buildInterfaceDefinition( const classMembers = node.fields ?.map((fieldNode) => { - const typeToUse = buildTypeMetadata(fieldNode.type, schema, config); - - const annotations = buildAnnotations({ - config, - definitionNode: fieldNode, - }); - const fieldDefinition = buildFieldDefinition( - fieldNode, + const typeMetadata = buildTypeMetadata(fieldNode.type, schema, config); + return buildFieldDefinition( node, + fieldNode, schema, config, + typeMetadata, + Boolean(fieldNode.arguments?.length), ); - const fieldText = indent( - `${fieldDefinition}: ${typeToUse.typeName}${ - typeToUse.isNullable ? "?" : "" - }`, - 2, - ); - return `${annotations}${fieldText}`; }) .join("\n"); @@ -56,7 +46,11 @@ export function buildInterfaceDefinition( config, definitionNode: node, }); - return `${annotations}interface ${node.name.value} { + + const interfacesToInherit = getDependentInterfaceNames(node); + const interfaceInheritance = `${interfacesToInherit.length ? ` : ${interfacesToInherit.join(", ")}` : ""}`; + + return `${annotations}interface ${node.name.value}${interfaceInheritance} { ${classMembers} }`; } diff --git a/src/definitions/object.ts b/src/definitions/object.ts index 4f8893e..b3ddf3b 100644 --- a/src/definitions/object.ts +++ b/src/definitions/object.ts @@ -12,24 +12,22 @@ limitations under the License. */ import { + FieldDefinitionNode, GraphQLSchema, isInputObjectType, - isInterfaceType, ObjectTypeDefinitionNode, } from "graphql"; import { buildAnnotations } from "../helpers/build-annotations"; -import { indent } from "@graphql-codegen/visitor-plugin-common"; import { buildTypeMetadata } from "../helpers/build-type-metadata"; import { shouldIncludeTypeDefinition } from "../helpers/should-include-type-definition"; import { getDependentInterfaceNames, getDependentUnionsForType, } from "../helpers/dependent-type-utils"; -import { isResolverType } from "../helpers/is-resolver-type"; import { buildFieldDefinition } from "../helpers/build-field-definition"; -import { isExternalField } from "../helpers/is-external-field"; import { CodegenConfigWithDefaults } from "../helpers/build-config-with-defaults"; import { inputTypeHasMatchingOutputType } from "../helpers/input-type-has-matching-output-type"; +import { findTypeInResolverClassesConfig } from "../helpers/findTypeInResolverClassesConfig"; export function buildObjectTypeDefinition( node: ObjectTypeDefinitionNode, @@ -53,16 +51,6 @@ export function buildObjectTypeDefinition( : dependentInterfaces; const interfaceInheritance = `${interfacesToInherit.length ? ` : ${interfacesToInherit.join(", ")}` : ""}`; - if (isResolverType(node, config)) { - return `${annotations}@GraphQLIgnore\ninterface ${name}${interfaceInheritance} { -${getDataClassMembers({ node, schema, config })} -} - -${annotations}@GraphQLIgnore\ninterface ${name}CompletableFuture { -${getDataClassMembers({ node, schema, config, completableFuture: true })} -}`; - } - const potentialMatchingInputType = schema.getType(`${name}Input`); const typeWillBeConsolidated = isInputObjectType(potentialMatchingInputType) && @@ -71,6 +59,50 @@ ${getDataClassMembers({ node, schema, config, completableFuture: true })} const outputRestrictionAnnotation = typeWillBeConsolidated ? "" : "@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])\n"; + + const typeInResolverClassesConfig = findTypeInResolverClassesConfig( + node, + config, + ); + const shouldGenerateFunctions = Boolean( + typeInResolverClassesConfig || + node.fields?.some((fieldNode) => fieldNode.arguments?.length), + ); + if (shouldGenerateFunctions) { + const fieldsWithNoArguments = node.fields?.filter( + (fieldNode) => !fieldNode.arguments?.length, + ); + const constructor = + !typeInResolverClassesConfig && fieldsWithNoArguments?.length + ? `(\n${fieldsWithNoArguments + .map((fieldNode) => { + const typeMetadata = buildTypeMetadata( + fieldNode.type, + schema, + config, + ); + return buildFieldDefinition( + node, + fieldNode, + schema, + config, + typeMetadata, + ); + }) + .join(",\n")}\n)` + : ""; + + const fieldsWithArguments = node.fields?.filter( + (fieldNode) => fieldNode.arguments?.length, + ); + const fieldNodes = typeInResolverClassesConfig + ? node.fields + : fieldsWithArguments; + return `${annotations}${outputRestrictionAnnotation}open class ${name}${constructor}${interfaceInheritance} { +${getDataClassMembers({ node, fieldNodes, schema, config, shouldGenerateFunctions })} +}`; + } + return `${annotations}${outputRestrictionAnnotation}data class ${name}( ${getDataClassMembers({ node, schema, config })} )${interfaceInheritance}`; @@ -78,50 +110,28 @@ ${getDataClassMembers({ node, schema, config })} function getDataClassMembers({ node, + fieldNodes, schema, config, - completableFuture, + shouldGenerateFunctions, }: { node: ObjectTypeDefinitionNode; + fieldNodes?: readonly FieldDefinitionNode[]; schema: GraphQLSchema; config: CodegenConfigWithDefaults; - completableFuture?: boolean; + shouldGenerateFunctions?: boolean; }) { - const resolverType = isResolverType(node, config); - - return node.fields + return (fieldNodes ?? node.fields) ?.map((fieldNode) => { const typeMetadata = buildTypeMetadata(fieldNode.type, schema, config); - const shouldOverrideField = - !completableFuture && - node.interfaces?.some((interfaceNode) => { - const typeNode = schema.getType(interfaceNode.name.value); - return ( - isInterfaceType(typeNode) && - typeNode.astNode?.fields?.some( - (field) => field.name.value === fieldNode.name.value, - ) - ); - }); - const fieldDefinition = buildFieldDefinition( - fieldNode, + return buildFieldDefinition( node, + fieldNode, schema, config, - completableFuture, - ); - const completableFutureDefinition = `java.util.concurrent.CompletableFuture<${typeMetadata.typeName}${typeMetadata.isNullable ? "?" : ""}>`; - const defaultDefinition = `${typeMetadata.typeName}${isExternalField(fieldNode) ? (typeMetadata.isNullable ? "?" : "") : typeMetadata.defaultValue}`; - const field = indent( - `${shouldOverrideField ? "override " : ""}${fieldDefinition}: ${completableFuture ? completableFutureDefinition : defaultDefinition}`, - 2, - ); - const annotations = buildAnnotations({ - config, - definitionNode: fieldNode, typeMetadata, - }); - return `${annotations}${field}`; + shouldGenerateFunctions, + ); }) - .join(`${resolverType ? "" : ","}\n`); + .join(`${shouldGenerateFunctions ? "" : ","}\n`); } diff --git a/src/helpers/build-config-with-defaults.ts b/src/helpers/build-config-with-defaults.ts index 644ec93..7f08012 100644 --- a/src/helpers/build-config-with-defaults.ts +++ b/src/helpers/build-config-with-defaults.ts @@ -15,13 +15,6 @@ export function buildConfigWithDefaults( "com.expediagroup.graphql.generator.annotations.*", ...(config.extraImports ?? []), ], - extraResolverArguments: [ - { - argumentName: "dataFetchingEnvironment", - argumentType: "graphql.schema.DataFetchingEnvironment", - }, - ...(config.extraResolverArguments ?? []), - ], } as const satisfies GraphQLKotlinCodegenConfig; } diff --git a/src/helpers/build-field-definition.ts b/src/helpers/build-field-definition.ts index e368172..e06c2ce 100644 --- a/src/helpers/build-field-definition.ts +++ b/src/helpers/build-field-definition.ts @@ -11,57 +11,144 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { buildTypeMetadata } from "./build-type-metadata"; +import { buildTypeMetadata, TypeMetadata } from "./build-type-metadata"; import { FieldDefinitionNode, GraphQLSchema, InterfaceTypeDefinitionNode, Kind, ObjectTypeDefinitionNode, + isInterfaceType, } from "graphql"; -import { isResolverType } from "./is-resolver-type"; -import { isExternalField } from "./is-external-field"; import { CodegenConfigWithDefaults } from "./build-config-with-defaults"; +import { indent } from "@graphql-codegen/visitor-plugin-common"; +import { buildAnnotations } from "./build-annotations"; +import { findTypeInResolverClassesConfig } from "./findTypeInResolverClassesConfig"; export function buildFieldDefinition( + node: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode, fieldNode: FieldDefinitionNode, - definitionNode: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode, schema: GraphQLSchema, config: CodegenConfigWithDefaults, - completableFuture?: boolean, + typeMetadata: TypeMetadata, + shouldGenerateFunctions?: boolean, ) { - const shouldUseFunction = - isResolverType(definitionNode, config) && !isExternalField(fieldNode); - const modifier = shouldUseFunction - ? completableFuture - ? "fun" - : "suspend fun" - : "val"; - const existingFieldArguments = fieldNode.arguments?.map((arg) => { - const typeMetadata = buildTypeMetadata(arg.type, schema, config); - return `${arg.name.value}: ${typeMetadata.typeName}${arg.type.kind === Kind.NON_NULL_TYPE ? "" : "?"}`; + const modifier = buildFieldModifier(node, fieldNode, schema, config); + const fieldArguments = buildFieldArguments(node, fieldNode, schema, config); + const fieldDefinition = `${modifier} ${fieldNode.name.value}${fieldArguments}`; + const annotations = buildAnnotations({ + config, + definitionNode: fieldNode, + typeMetadata, }); - const additionalFieldArguments = config.extraResolverArguments - ?.map((resolverArgument) => { - const { argumentName, argumentType } = resolverArgument; - const shouldIncludeArg = - !("typeNames" in resolverArgument) || - !resolverArgument.typeNames || - resolverArgument.typeNames.some( - (typeName) => typeName === definitionNode.name.value, - ); - return shouldUseFunction && shouldIncludeArg - ? `${argumentName}: ${argumentType}` - : undefined; - }) - .filter(Boolean); - const allFieldArguments = existingFieldArguments?.concat( - additionalFieldArguments ?? [], + if (node.kind === Kind.INTERFACE_TYPE_DEFINITION) { + return buildInterfaceFieldDefinition( + fieldDefinition, + typeMetadata, + annotations, + ); + } + + const notImplementedError = ` = throw NotImplementedError("${node.name.value}.${fieldNode.name.value} must be implemented.")`; + const defaultFunctionValue = `${typeMetadata.isNullable ? "?" : ""}${notImplementedError}`; + const defaultValue = shouldGenerateFunctions + ? defaultFunctionValue + : typeMetadata.defaultValue; + const defaultDefinition = `${typeMetadata.typeName}${defaultValue}`; + const typeInResolverClassesConfig = findTypeInResolverClassesConfig( + node, + config, ); - const fieldArguments = allFieldArguments?.length + const isCompletableFuture = + typeInResolverClassesConfig?.classMethods === "COMPLETABLE_FUTURE"; + const completableFutureDefinition = `java.util.concurrent.CompletableFuture<${typeMetadata.typeName}${typeMetadata.isNullable ? "?" : ""}>${notImplementedError}`; + const field = indent( + `${fieldDefinition}: ${isCompletableFuture ? completableFutureDefinition : defaultDefinition}`, + 2, + ); + return `${annotations}${field}`; +} + +function buildFieldModifier( + node: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode, + fieldNode: FieldDefinitionNode, + schema: GraphQLSchema, + config: CodegenConfigWithDefaults, +) { + const typeInResolverClassesConfig = findTypeInResolverClassesConfig( + node, + config, + ); + const shouldOverrideField = shouldModifyFieldWithOverride( + node, + fieldNode, + schema, + ); + if (!typeInResolverClassesConfig && !fieldNode.arguments?.length) { + return shouldOverrideField ? "override val" : "val"; + } + const functionModifier = + typeInResolverClassesConfig?.classMethods === "SUSPEND" ? "suspend " : ""; + if (node.kind === Kind.INTERFACE_TYPE_DEFINITION) { + return `${functionModifier}fun`; + } + const isCompletableFuture = + typeInResolverClassesConfig?.classMethods === "COMPLETABLE_FUTURE"; + if (shouldOverrideField && !isCompletableFuture) { + return "override fun"; + } + return `open ${functionModifier}fun`; +} + +function buildFieldArguments( + node: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode, + fieldNode: FieldDefinitionNode, + schema: GraphQLSchema, + config: CodegenConfigWithDefaults, +) { + const typeIsInResolverClasses = findTypeInResolverClassesConfig(node, config); + if (!typeIsInResolverClasses && !fieldNode.arguments?.length) { + return ""; + } + const existingFieldArguments = fieldNode.arguments?.map((arg) => { + const argMetadata = buildTypeMetadata(arg.type, schema, config); + return `${arg.name.value}: ${argMetadata.typeName}${arg.type.kind === Kind.NON_NULL_TYPE ? "" : "?"}`; + }); + const dataFetchingEnvironmentArgument = + "dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment"; + const extraFieldArguments = [dataFetchingEnvironmentArgument]; + const allFieldArguments = existingFieldArguments?.concat(extraFieldArguments); + return allFieldArguments?.length ? `(${allFieldArguments?.join(", ")})` - : shouldUseFunction - ? "()" - : ""; - return `${modifier} ${fieldNode.name.value}${fieldArguments}`; + : "()"; +} + +function shouldModifyFieldWithOverride( + node: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode, + fieldNode: FieldDefinitionNode, + schema: GraphQLSchema, +) { + return node.interfaces?.some((interfaceNode) => { + const typeNode = schema.getType(interfaceNode.name.value); + return ( + isInterfaceType(typeNode) && + typeNode.astNode?.fields?.some( + (field) => field.name.value === fieldNode.name.value, + ) + ); + }); +} + +function buildInterfaceFieldDefinition( + fieldDefinition: string, + typeMetadata: TypeMetadata, + annotations: string, +) { + const fieldText = indent( + `${fieldDefinition}: ${typeMetadata.typeName}${ + typeMetadata.isNullable ? "?" : "" + }`, + 2, + ); + return `${annotations}${fieldText}`; } diff --git a/src/helpers/dependent-type-utils.ts b/src/helpers/dependent-type-utils.ts index 57724c9..17e7e18 100644 --- a/src/helpers/dependent-type-utils.ts +++ b/src/helpers/dependent-type-utils.ts @@ -42,7 +42,7 @@ function getFieldTypeName(fieldType: TypeNode) { } export function getDependentInterfaceNames(node: TypeDefinitionNode) { - return node.kind === Kind.OBJECT_TYPE_DEFINITION + return "interfaces" in node ? node.interfaces?.map((interfaceNode) => interfaceNode.name.value) ?? [] : []; } diff --git a/src/helpers/findTypeInResolverClassesConfig.ts b/src/helpers/findTypeInResolverClassesConfig.ts new file mode 100644 index 0000000..2f42d15 --- /dev/null +++ b/src/helpers/findTypeInResolverClassesConfig.ts @@ -0,0 +1,11 @@ +import { InterfaceTypeDefinitionNode, ObjectTypeDefinitionNode } from "graphql"; +import { CodegenConfigWithDefaults } from "./build-config-with-defaults"; + +export function findTypeInResolverClassesConfig( + node: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode, + config: CodegenConfigWithDefaults, +) { + return config.resolverClasses?.find( + (resolverClass) => resolverClass.typeName === node.name.value, + ); +} diff --git a/src/helpers/is-external-field.ts b/src/helpers/is-external-field.ts deleted file mode 100644 index 3f75171..0000000 --- a/src/helpers/is-external-field.ts +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright 2024 Expedia, Inc. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - https://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { FieldDefinitionNode } from "graphql"; - -export function isExternalField(fieldNode: FieldDefinitionNode) { - return fieldNode.directives?.some( - (directive) => directive.name.value === "external", - ); -} diff --git a/src/helpers/is-resolver-type.ts b/src/helpers/is-resolver-type.ts deleted file mode 100644 index 559ed46..0000000 --- a/src/helpers/is-resolver-type.ts +++ /dev/null @@ -1,25 +0,0 @@ -/* -Copyright 2024 Expedia, Inc. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - https://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import { InterfaceTypeDefinitionNode, ObjectTypeDefinitionNode } from "graphql"; -import { CodegenConfigWithDefaults } from "./build-config-with-defaults"; - -export function isResolverType( - node: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode, - config: CodegenConfigWithDefaults, -) { - return ( - node.fields?.some((fieldNode) => fieldNode.arguments?.length) || - config.resolverTypes?.includes(node.name.value) - ); -} diff --git a/test/unit/should_consolidate_input_and_output_types/expected.kt b/test/unit/should_consolidate_input_and_output_types/expected.kt index 366755a..441f9a6 100644 --- a/test/unit/should_consolidate_input_and_output_types/expected.kt +++ b/test/unit/should_consolidate_input_and_output_types/expected.kt @@ -68,14 +68,9 @@ data class MyTypeToConsolidateInputParent( val field: MyTypeToConsolidate? = null ) -@GraphQLIgnore -interface MyTypeToConsolidateParent2 { - suspend fun field(input: MyTypeToConsolidate, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = null -} - -@GraphQLIgnore -interface MyTypeToConsolidateParent2CompletableFuture { - fun field(input: MyTypeToConsolidate, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture +@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT]) +open class MyTypeToConsolidateParent2 { + open fun field(input: MyTypeToConsolidate, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = throw NotImplementedError("MyTypeToConsolidateParent2.field must be implemented.") } @GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT]) diff --git a/test/unit/should_generate_classes_for_types_with_field_args/expected.kt b/test/unit/should_generate_classes_for_types_with_field_args/expected.kt new file mode 100644 index 0000000..4d0101f --- /dev/null +++ b/test/unit/should_generate_classes_for_types_with_field_args/expected.kt @@ -0,0 +1,43 @@ +package com.kotlin.generated + +import com.expediagroup.graphql.generator.annotations.* + +@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT]) +open class TypeWithOnlyFieldArgs { + open fun nullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = throw NotImplementedError("TypeWithOnlyFieldArgs.nullableResolver must be implemented.") + open fun nonNullableResolver(arg: InputTypeForResolver, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String = throw NotImplementedError("TypeWithOnlyFieldArgs.nonNullableResolver must be implemented.") +} + +@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT]) +open class HybridType( + val nullableField: String? = null, + val nonNullableField: String +) { + open fun nullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = throw NotImplementedError("HybridType.nullableResolver must be implemented.") + open fun nonNullableResolver(arg: InputTypeForResolver, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String = throw NotImplementedError("HybridType.nonNullableResolver must be implemented.") +} + +@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.INPUT_OBJECT]) +data class InputTypeForResolver( + val field: String? = null +) + +interface HybridInterface { + val field1: String? + val field2: String + fun nullableListResolver(arg1: Int?, arg2: Int, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): List? + fun nonNullableListResolver(arg1: Int, arg2: Int?, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): List +} + +@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT]) +open class TypeImplementingInterface( + override val field1: String? = null, + override val field2: String, + val booleanField1: Boolean? = null, + val booleanField2: Boolean = false, + val integerField1: Int? = null, + val integerField2: Int +) : HybridInterface { + override fun nullableListResolver(arg1: Int?, arg2: Int, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): List? = throw NotImplementedError("TypeImplementingInterface.nullableListResolver must be implemented.") + override fun nonNullableListResolver(arg1: Int, arg2: Int?, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): List = throw NotImplementedError("TypeImplementingInterface.nonNullableListResolver must be implemented.") +} diff --git a/test/unit/should_generate_field_resolver_interfaces/schema.graphql b/test/unit/should_generate_classes_for_types_with_field_args/schema.graphql similarity index 55% rename from test/unit/should_generate_field_resolver_interfaces/schema.graphql rename to test/unit/should_generate_classes_for_types_with_field_args/schema.graphql index ff57062..117cff1 100644 --- a/test/unit/should_generate_field_resolver_interfaces/schema.graphql +++ b/test/unit/should_generate_classes_for_types_with_field_args/schema.graphql @@ -1,22 +1,27 @@ -type Query { - nullableField: FieldType - nonNullableField: FieldType! +type TypeWithOnlyFieldArgs { nullableResolver(arg: String!): String - nonNullableResolver(arg: InputTypeGenerateFieldResolverInterfaces!): String! + nonNullableResolver(arg: InputTypeForResolver!): String! } -input InputTypeGenerateFieldResolverInterfaces { +type HybridType { + nullableField: String + nonNullableField: String! + nullableResolver(arg: String!): String + nonNullableResolver(arg: InputTypeForResolver!): String! +} + +input InputTypeForResolver { field: String } -interface MyFieldInterface { +interface HybridInterface { field1: String field2: String! nullableListResolver(arg1: Int, arg2: Int!): [String] nonNullableListResolver(arg1: Int!, arg2: Int): [String!]! } -type FieldType implements MyFieldInterface { +type TypeImplementingInterface implements HybridInterface { field1: String field2: String! booleanField1: Boolean diff --git a/test/unit/should_generate_field_resolver_interfaces/expected.kt b/test/unit/should_generate_field_resolver_interfaces/expected.kt deleted file mode 100644 index 310a944..0000000 --- a/test/unit/should_generate_field_resolver_interfaces/expected.kt +++ /dev/null @@ -1,55 +0,0 @@ -package com.kotlin.generated - -import com.expediagroup.graphql.generator.annotations.* - -@GraphQLIgnore -interface Query { - suspend fun nullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): FieldType? = null - suspend fun nonNullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): FieldType - suspend fun nullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = null - suspend fun nonNullableResolver(arg: InputTypeGenerateFieldResolverInterfaces, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String -} - -@GraphQLIgnore -interface QueryCompletableFuture { - fun nullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture - fun nonNullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture - fun nullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture - fun nonNullableResolver(arg: InputTypeGenerateFieldResolverInterfaces, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture -} - -@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.INPUT_OBJECT]) -data class InputTypeGenerateFieldResolverInterfaces( - val field: String? = null -) - -interface MyFieldInterface { - suspend fun field1(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? - suspend fun field2(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String - suspend fun nullableListResolver(arg1: Int?, arg2: Int, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): List? - suspend fun nonNullableListResolver(arg1: Int, arg2: Int?, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): List -} - -@GraphQLIgnore -interface FieldType : MyFieldInterface { - override suspend fun field1(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = null - override suspend fun field2(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String - suspend fun booleanField1(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): Boolean? = null - suspend fun booleanField2(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): Boolean = false - suspend fun integerField1(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): Int? = null - suspend fun integerField2(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): Int - override suspend fun nullableListResolver(arg1: Int?, arg2: Int, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): List? = null - override suspend fun nonNullableListResolver(arg1: Int, arg2: Int?, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): List = emptyList() -} - -@GraphQLIgnore -interface FieldTypeCompletableFuture { - fun field1(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture - fun field2(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture - fun booleanField1(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture - fun booleanField2(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture - fun integerField1(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture - fun integerField2(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture - fun nullableListResolver(arg1: Int?, arg2: Int, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture?> - fun nonNullableListResolver(arg1: Int, arg2: Int?, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture> -} diff --git a/test/unit/should_generate_interfaces_with_inheritance/expected.kt b/test/unit/should_generate_interfaces_with_inheritance/expected.kt index 9868688..b9af0ab 100644 --- a/test/unit/should_generate_interfaces_with_inheritance/expected.kt +++ b/test/unit/should_generate_interfaces_with_inheritance/expected.kt @@ -8,13 +8,20 @@ interface InterfaceWithInheritance { val field2: String } -@GraphQLDescription("A description for MyInterfaceImplementation") +@GraphQLDescription("A description for MyImplementation") @GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT]) -data class MyInterfaceImplementation( +data class MyImplementation( override val field: String? = null, override val field2: String ) : InterfaceWithInheritance +@GraphQLDescription("A description for MyInterfaceImplementation") +interface MyInterfaceImplementation : InterfaceWithInheritance { + override val field: String? + override val field2: String + val field3: Int? +} + interface InheritedInterface1 { val field: String? } diff --git a/test/unit/should_generate_interfaces_with_inheritance/schema.graphql b/test/unit/should_generate_interfaces_with_inheritance/schema.graphql index 90826cb..283d771 100644 --- a/test/unit/should_generate_interfaces_with_inheritance/schema.graphql +++ b/test/unit/should_generate_interfaces_with_inheritance/schema.graphql @@ -4,10 +4,17 @@ interface InterfaceWithInheritance { field2: String! } +"A description for MyImplementation" +type MyImplementation implements InterfaceWithInheritance { + field: String + field2: String! +} + "A description for MyInterfaceImplementation" -type MyInterfaceImplementation implements InterfaceWithInheritance { +interface MyInterfaceImplementation implements InterfaceWithInheritance { field: String field2: String! + field3: Int } interface InheritedInterface1 { diff --git a/test/unit/should_honor_extraResolverArguments_config/codegen.config.ts b/test/unit/should_honor_extraResolverArguments_config/codegen.config.ts deleted file mode 100644 index 6906094..0000000 --- a/test/unit/should_honor_extraResolverArguments_config/codegen.config.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { GraphQLKotlinCodegenConfig } from "../../../src/plugin"; - -export default { - resolverTypes: ["MyIncludedExtraFieldArgsType"], - extraResolverArguments: [ - { - argumentName: "myExtraFieldArg", - argumentType: "String", - typeNames: ["MyExtraFieldArgsType", "MyIncludedExtraFieldArgsType"], - }, - ], -} satisfies GraphQLKotlinCodegenConfig; diff --git a/test/unit/should_honor_extraResolverArguments_config/expected.kt b/test/unit/should_honor_extraResolverArguments_config/expected.kt deleted file mode 100644 index 64a985e..0000000 --- a/test/unit/should_honor_extraResolverArguments_config/expected.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.kotlin.generated - -import com.expediagroup.graphql.generator.annotations.* - -@GraphQLIgnore -interface MyExtraFieldArgsType { - suspend fun myField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment, myExtraFieldArg: String): String? = null - suspend fun fieldWithArgs(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment, myExtraFieldArg: String): String -} - -@GraphQLIgnore -interface MyExtraFieldArgsTypeCompletableFuture { - fun myField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment, myExtraFieldArg: String): java.util.concurrent.CompletableFuture - fun fieldWithArgs(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment, myExtraFieldArg: String): java.util.concurrent.CompletableFuture -} - -@GraphQLIgnore -interface MyIncludedExtraFieldArgsType { - suspend fun myField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment, myExtraFieldArg: String): String? = null - suspend fun myOtherField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment, myExtraFieldArg: String): String? = null -} - -@GraphQLIgnore -interface MyIncludedExtraFieldArgsTypeCompletableFuture { - fun myField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment, myExtraFieldArg: String): java.util.concurrent.CompletableFuture - fun myOtherField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment, myExtraFieldArg: String): java.util.concurrent.CompletableFuture -} - -@GraphQLIgnore -interface MyOtherType { - suspend fun myField(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String - suspend fun myOtherField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = null -} - -@GraphQLIgnore -interface MyOtherTypeCompletableFuture { - fun myField(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture - fun myOtherField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture -} diff --git a/test/unit/should_honor_extraResolverArguments_config/schema.graphql b/test/unit/should_honor_extraResolverArguments_config/schema.graphql deleted file mode 100644 index 543b7c6..0000000 --- a/test/unit/should_honor_extraResolverArguments_config/schema.graphql +++ /dev/null @@ -1,14 +0,0 @@ -type MyExtraFieldArgsType { - myField: String - fieldWithArgs(arg: String!): String! -} - -type MyIncludedExtraFieldArgsType { - myField: String - myOtherField: String -} - -type MyOtherType { - myField(arg: String!): String! - myOtherField: String -} diff --git a/test/unit/should_honor_resolverClasses_config/codegen.config.ts b/test/unit/should_honor_resolverClasses_config/codegen.config.ts new file mode 100644 index 0000000..1df958f --- /dev/null +++ b/test/unit/should_honor_resolverClasses_config/codegen.config.ts @@ -0,0 +1,27 @@ +import { GraphQLKotlinCodegenConfig } from "../../../src/plugin"; + +export default { + resolverClasses: [ + { + typeName: "MyIncludedResolverType", + }, + { + typeName: "MyIncludedResolverTypeWithNoFieldArgs", + }, + { + typeName: "MySuspendResolverType", + classMethods: "SUSPEND", + }, + { + typeName: "MyCompletableFutureResolverType", + classMethods: "COMPLETABLE_FUTURE", + }, + { + typeName: "MyIncludedInterface", + }, + { + typeName: "MyIncludedInterfaceSuspend", + classMethods: "SUSPEND", + }, + ], +} satisfies GraphQLKotlinCodegenConfig; diff --git a/test/unit/should_honor_resolverClasses_config/expected.kt b/test/unit/should_honor_resolverClasses_config/expected.kt new file mode 100644 index 0000000..a4944d1 --- /dev/null +++ b/test/unit/should_honor_resolverClasses_config/expected.kt @@ -0,0 +1,57 @@ +package com.kotlin.generated + +import com.expediagroup.graphql.generator.annotations.* + +@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT]) +open class MyIncludedResolverType { + open fun nullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = throw NotImplementedError("MyIncludedResolverType.nullableField must be implemented.") + open fun nonNullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String = throw NotImplementedError("MyIncludedResolverType.nonNullableField must be implemented.") + open fun nullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = throw NotImplementedError("MyIncludedResolverType.nullableResolver must be implemented.") + open fun nonNullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String = throw NotImplementedError("MyIncludedResolverType.nonNullableResolver must be implemented.") + open fun nullableListResolver(arg1: Int?, arg2: Int, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): List? = throw NotImplementedError("MyIncludedResolverType.nullableListResolver must be implemented.") + open fun nonNullableListResolver(arg1: Int, arg2: Int?, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): List = throw NotImplementedError("MyIncludedResolverType.nonNullableListResolver must be implemented.") +} + +@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT]) +open class MyIncludedResolverTypeWithNoFieldArgs { + open fun nullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = throw NotImplementedError("MyIncludedResolverTypeWithNoFieldArgs.nullableField must be implemented.") + open fun nonNullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String = throw NotImplementedError("MyIncludedResolverTypeWithNoFieldArgs.nonNullableField must be implemented.") +} + +@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT]) +open class MySuspendResolverType { + open suspend fun nullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = throw NotImplementedError("MySuspendResolverType.nullableField must be implemented.") + open suspend fun nonNullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String = throw NotImplementedError("MySuspendResolverType.nonNullableField must be implemented.") + open suspend fun nullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = throw NotImplementedError("MySuspendResolverType.nullableResolver must be implemented.") + open suspend fun nonNullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String = throw NotImplementedError("MySuspendResolverType.nonNullableResolver must be implemented.") + open suspend fun nullableListResolver(arg1: Int?, arg2: Int, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): List? = throw NotImplementedError("MySuspendResolverType.nullableListResolver must be implemented.") + open suspend fun nonNullableListResolver(arg1: Int, arg2: Int?, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): List = throw NotImplementedError("MySuspendResolverType.nonNullableListResolver must be implemented.") +} + +@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT]) +open class MyCompletableFutureResolverType { + open fun nullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture = throw NotImplementedError("MyCompletableFutureResolverType.nullableField must be implemented.") + open fun nonNullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture = throw NotImplementedError("MyCompletableFutureResolverType.nonNullableField must be implemented.") + open fun nullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture = throw NotImplementedError("MyCompletableFutureResolverType.nullableResolver must be implemented.") + open fun nonNullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture = throw NotImplementedError("MyCompletableFutureResolverType.nonNullableResolver must be implemented.") + open fun nullableListResolver(arg1: Int?, arg2: Int, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture?> = throw NotImplementedError("MyCompletableFutureResolverType.nullableListResolver must be implemented.") + open fun nonNullableListResolver(arg1: Int, arg2: Int?, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture> = throw NotImplementedError("MyCompletableFutureResolverType.nonNullableListResolver must be implemented.") +} + +@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT]) +data class MyExcludedResolverType( + val nullableField: String? = null, + val nonNullableField: String +) + +interface MyIncludedInterface { + fun field(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? +} + +interface MyIncludedInterfaceSuspend { + suspend fun field(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? +} + +interface MyExcludedInterface { + val field: String? +} diff --git a/test/unit/should_honor_resolverClasses_config/schema.graphql b/test/unit/should_honor_resolverClasses_config/schema.graphql new file mode 100644 index 0000000..03c3279 --- /dev/null +++ b/test/unit/should_honor_resolverClasses_config/schema.graphql @@ -0,0 +1,48 @@ +type MyIncludedResolverType { + nullableField: String + nonNullableField: String! + nullableResolver(arg: String!): String + nonNullableResolver(arg: String!): String! + nullableListResolver(arg1: Int, arg2: Int!): [String] + nonNullableListResolver(arg1: Int!, arg2: Int): [String!]! +} + +type MyIncludedResolverTypeWithNoFieldArgs { + nullableField: String + nonNullableField: String! +} + +type MySuspendResolverType { + nullableField: String + nonNullableField: String! + nullableResolver(arg: String!): String + nonNullableResolver(arg: String!): String! + nullableListResolver(arg1: Int, arg2: Int!): [String] + nonNullableListResolver(arg1: Int!, arg2: Int): [String!]! +} + +type MyCompletableFutureResolverType { + nullableField: String + nonNullableField: String! + nullableResolver(arg: String!): String + nonNullableResolver(arg: String!): String! + nullableListResolver(arg1: Int, arg2: Int!): [String] + nonNullableListResolver(arg1: Int!, arg2: Int): [String!]! +} + +type MyExcludedResolverType { + nullableField: String + nonNullableField: String! +} + +interface MyIncludedInterface { + field: String +} + +interface MyIncludedInterfaceSuspend { + field: String +} + +interface MyExcludedInterface { + field: String +} diff --git a/test/unit/should_honor_resolverTypes_config/codegen.config.ts b/test/unit/should_honor_resolverTypes_config/codegen.config.ts deleted file mode 100644 index f6bf307..0000000 --- a/test/unit/should_honor_resolverTypes_config/codegen.config.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { GraphQLKotlinCodegenConfig } from "../../../src/plugin"; - -export default { - resolverTypes: ["MyIncludedResolverType", "MyIncludedInterface"], -} satisfies GraphQLKotlinCodegenConfig; diff --git a/test/unit/should_honor_resolverTypes_config/expected.kt b/test/unit/should_honor_resolverTypes_config/expected.kt deleted file mode 100644 index 6dfa8d9..0000000 --- a/test/unit/should_honor_resolverTypes_config/expected.kt +++ /dev/null @@ -1,45 +0,0 @@ -package com.kotlin.generated - -import com.expediagroup.graphql.generator.annotations.* - -@GraphQLIgnore -interface MyResolverType { - suspend fun nullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = null - suspend fun nonNullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String - suspend fun nullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = null - suspend fun nonNullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String -} - -@GraphQLIgnore -interface MyResolverTypeCompletableFuture { - fun nullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture - fun nonNullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture - fun nullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture - fun nonNullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture -} - -@GraphQLIgnore -interface MyIncludedResolverType { - suspend fun nullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = null - suspend fun nonNullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String -} - -@GraphQLIgnore -interface MyIncludedResolverTypeCompletableFuture { - fun nullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture - fun nonNullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture -} - -@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT]) -data class MyExcludedResolverType( - val nullableField: String? = null, - val nonNullableField: String -) - -interface MyIncludedInterface { - suspend fun field(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? -} - -interface MyExcludedInterface { - val field: String? -} diff --git a/test/unit/should_honor_resolverTypes_config/schema.graphql b/test/unit/should_honor_resolverTypes_config/schema.graphql deleted file mode 100644 index d7f5372..0000000 --- a/test/unit/should_honor_resolverTypes_config/schema.graphql +++ /dev/null @@ -1,24 +0,0 @@ -type MyResolverType { - nullableField: String - nonNullableField: String! - nullableResolver(arg: String!): String - nonNullableResolver(arg: String!): String! -} - -type MyIncludedResolverType { - nullableField: String - nonNullableField: String! -} - -type MyExcludedResolverType { - nullableField: String - nonNullableField: String! -} - -interface MyIncludedInterface { - field: String -} - -interface MyExcludedInterface { - field: String -} diff --git a/test/unit/should_replace_federation_directives/expected.kt b/test/unit/should_replace_federation_directives/expected.kt index 23d18c0..0a6a4e1 100644 --- a/test/unit/should_replace_federation_directives/expected.kt +++ b/test/unit/should_replace_federation_directives/expected.kt @@ -12,18 +12,10 @@ data class FederatedType( @com.expediagroup.graphql.generator.federation.directives.ExtendsDirective @com.expediagroup.graphql.generator.federation.directives.KeyDirective(com.expediagroup.graphql.generator.federation.directives.FieldSet("some other field")) -@GraphQLIgnore -interface FederatedTypeResolver { - suspend fun field(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String - @com.expediagroup.graphql.generator.federation.directives.ExternalDirective - val field2: String? -} - -@com.expediagroup.graphql.generator.federation.directives.ExtendsDirective -@com.expediagroup.graphql.generator.federation.directives.KeyDirective(com.expediagroup.graphql.generator.federation.directives.FieldSet("some other field")) -@GraphQLIgnore -interface FederatedTypeResolverCompletableFuture { - fun field(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture +@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT]) +open class FederatedTypeResolver( @com.expediagroup.graphql.generator.federation.directives.ExternalDirective - val field2: java.util.concurrent.CompletableFuture + val field2: String? = null +) { + open fun field(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String = throw NotImplementedError("FederatedTypeResolver.field must be implemented.") }