Skip to content

Commit d1b1e2d

Browse files
committed
pass all tests
1 parent 564f22a commit d1b1e2d

File tree

7 files changed

+98
-39
lines changed

7 files changed

+98
-39
lines changed

src/config.ts

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -104,11 +104,13 @@ export const configSchema = object({
104104
),
105105
/**
106106
* Denotes types that should be generated as classes. Resolver classes can inherit from these to enforce a type contract.
107-
* @description Type names can be passed as strings to generate default functions.
108-
* Also, suspend functions or `java.util.concurrent.CompletableFuture` functions can be generated per type.
107+
* @description Type names can be optionally passed with the classMethods config to generate suspend functions or
108+
* `java.util.concurrent.CompletableFuture` functions.
109109
* @example
110110
* [
111-
* "MyResolverType",
111+
* {
112+
* typeName: "MyResolverType",
113+
* },
112114
* {
113115
* typeName: "MySuspendResolverType",
114116
* classMethods: "SUSPEND",
@@ -121,15 +123,12 @@ export const configSchema = object({
121123
*/
122124
resolverClasses: optional(
123125
array(
124-
union([
125-
string(),
126-
object({
127-
typeName: string(),
128-
classMethods: optional(
129-
union([literal("SUSPEND"), literal("COMPLETABLE_FUTURE")]),
130-
),
131-
}),
132-
]),
126+
object({
127+
typeName: string(),
128+
classMethods: optional(
129+
union([literal("SUSPEND"), literal("COMPLETABLE_FUTURE")]),
130+
),
131+
}),
133132
),
134133
),
135134
/**

src/definitions/object.ts

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
import { buildFieldDefinition } from "../helpers/build-field-definition";
2828
import { CodegenConfigWithDefaults } from "../helpers/build-config-with-defaults";
2929
import { inputTypeHasMatchingOutputType } from "../helpers/input-type-has-matching-output-type";
30+
import { findTypeInResolverClassesConfig } from "../helpers/findTypeInResolverClassesConfig";
3031

3132
export function buildObjectTypeDefinition(
3233
node: ObjectTypeDefinitionNode,
@@ -62,15 +63,16 @@ export function buildObjectTypeDefinition(
6263
const shouldGenerateFunctions = node.fields?.some(
6364
(fieldNode) => fieldNode.arguments?.length,
6465
);
66+
const typeInResolverClassesConfig = findTypeInResolverClassesConfig(
67+
node,
68+
config,
69+
);
6570
if (shouldGenerateFunctions) {
6671
const fieldsWithNoArguments = node.fields?.filter(
6772
(fieldNode) => !fieldNode.arguments?.length,
6873
);
69-
const resolverClassesContainsType = config.resolverClasses?.includes(
70-
node.name.value,
71-
);
7274
const constructor =
73-
!resolverClassesContainsType && fieldsWithNoArguments?.length
75+
!typeInResolverClassesConfig && fieldsWithNoArguments?.length
7476
? `(\n${fieldsWithNoArguments
7577
.map((fieldNode) => {
7678
const typeMetadata = buildTypeMetadata(
@@ -92,7 +94,7 @@ export function buildObjectTypeDefinition(
9294
const fieldsWithArguments = node.fields?.filter(
9395
(fieldNode) => fieldNode.arguments?.length,
9496
);
95-
const fieldNodes = resolverClassesContainsType
97+
const fieldNodes = typeInResolverClassesConfig
9698
? node.fields
9799
: fieldsWithArguments;
98100
return `${annotations}${outputRestrictionAnnotation}open class ${name}${constructor}${interfaceInheritance} {

src/helpers/build-field-definition.ts

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
import { CodegenConfigWithDefaults } from "./build-config-with-defaults";
2424
import { indent } from "@graphql-codegen/visitor-plugin-common";
2525
import { buildAnnotations } from "./build-annotations";
26+
import { findTypeInResolverClassesConfig } from "./findTypeInResolverClassesConfig";
2627

2728
export function buildFieldDefinition(
2829
node: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode,
@@ -48,13 +49,19 @@ export function buildFieldDefinition(
4849
);
4950
}
5051

51-
const defaultFunctionValue = `${typeMetadata.isNullable ? "?" : ""} = throw NotImplementedError("${node.name.value}.${fieldNode.name.value} must be implemented.")`;
52+
const notImplementedError = ` = throw NotImplementedError("${node.name.value}.${fieldNode.name.value} must be implemented.")`;
53+
const defaultFunctionValue = `${typeMetadata.isNullable ? "?" : ""}${notImplementedError}`;
5254
const defaultValue = shouldUseFunction
5355
? defaultFunctionValue
5456
: typeMetadata.defaultValue;
5557
const defaultDefinition = `${typeMetadata.typeName}${defaultValue}`;
56-
const completableFuture = false;
57-
const completableFutureDefinition = `java.util.concurrent.CompletableFuture<${typeMetadata.typeName}${typeMetadata.isNullable ? "?" : ""}>`;
58+
const typeInResolverClassesConfig = findTypeInResolverClassesConfig(
59+
node,
60+
config,
61+
);
62+
const completableFuture =
63+
typeInResolverClassesConfig?.classMethods === "COMPLETABLE_FUTURE";
64+
const completableFutureDefinition = `java.util.concurrent.CompletableFuture<${typeMetadata.typeName}${typeMetadata.isNullable ? "?" : ""}>${notImplementedError}`;
5865
const field = indent(
5966
`${fieldDefinition}: ${completableFuture ? completableFutureDefinition : defaultDefinition}`,
6067
2,
@@ -68,23 +75,27 @@ function buildFieldModifier(
6875
schema: GraphQLSchema,
6976
config: CodegenConfigWithDefaults,
7077
) {
71-
const resolverClassesContainsType = config.resolverClasses?.includes(
72-
node.name.value,
78+
const typeInResolverClassesConfig = findTypeInResolverClassesConfig(
79+
node,
80+
config,
7381
);
74-
const completableFuture = false;
82+
const completableFuture =
83+
typeInResolverClassesConfig?.classMethods === "COMPLETABLE_FUTURE";
7584
const shouldOverrideField =
7685
!completableFuture &&
7786
shouldModifyFieldWithOverride(node, fieldNode, schema);
78-
if (!resolverClassesContainsType && !fieldNode.arguments?.length) {
87+
if (!typeInResolverClassesConfig && !fieldNode.arguments?.length) {
7988
return shouldOverrideField ? "override val" : "val";
8089
}
81-
if (completableFuture || node.kind === Kind.INTERFACE_TYPE_DEFINITION) {
82-
return "fun";
90+
const functionModifier =
91+
typeInResolverClassesConfig?.classMethods === "SUSPEND" ? "suspend " : "";
92+
if (node.kind === Kind.INTERFACE_TYPE_DEFINITION) {
93+
return `${functionModifier}fun`;
8394
}
8495
if (shouldOverrideField) {
8596
return "override fun";
8697
}
87-
return "open fun";
98+
return `open ${functionModifier}fun`;
8899
}
89100

90101
function buildFieldArguments(
@@ -93,10 +104,8 @@ function buildFieldArguments(
93104
schema: GraphQLSchema,
94105
config: CodegenConfigWithDefaults,
95106
) {
96-
const resolverClassesContainsType = config.resolverClasses?.includes(
97-
node.name.value,
98-
);
99-
if (!resolverClassesContainsType && !fieldNode.arguments?.length) {
107+
const typeIsInResolverClasses = findTypeInResolverClassesConfig(node, config);
108+
if (!typeIsInResolverClasses && !fieldNode.arguments?.length) {
100109
return "";
101110
}
102111
const existingFieldArguments = fieldNode.arguments?.map((arg) => {
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { InterfaceTypeDefinitionNode, ObjectTypeDefinitionNode } from "graphql";
2+
import { CodegenConfigWithDefaults } from "./build-config-with-defaults";
3+
4+
export function findTypeInResolverClassesConfig(
5+
node: ObjectTypeDefinitionNode | InterfaceTypeDefinitionNode,
6+
config: CodegenConfigWithDefaults,
7+
) {
8+
return config.resolverClasses?.find(
9+
(resolverClass) => resolverClass.typeName === node.name.value,
10+
);
11+
}

test/unit/should_honor_resolverClasses_config/codegen.config.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ import { GraphQLKotlinCodegenConfig } from "../../../src/plugin";
22

33
export default {
44
resolverClasses: [
5-
"MyResolverType",
5+
{
6+
typeName: "MyIncludedResolverType",
7+
},
68
{
79
typeName: "MySuspendResolverType",
810
classMethods: "SUSPEND",
@@ -11,5 +13,12 @@ export default {
1113
typeName: "MyCompletableFutureResolverType",
1214
classMethods: "COMPLETABLE_FUTURE",
1315
},
16+
{
17+
typeName: "MyIncludedInterface",
18+
},
19+
{
20+
typeName: "MyIncludedInterfaceSuspend",
21+
classMethods: "SUSPEND",
22+
},
1423
],
1524
} satisfies GraphQLKotlinCodegenConfig;

test/unit/should_honor_resolverClasses_config/expected.kt

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ package com.kotlin.generated
33
import com.expediagroup.graphql.generator.annotations.*
44

55
@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
6-
open class MyResolverType {
7-
open fun nullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = throw NotImplementedError("MyResolverType.nullableField must be implemented.")
8-
open fun nonNullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String = throw NotImplementedError("MyResolverType.nonNullableField must be implemented.")
9-
open fun nullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = throw NotImplementedError("MyResolverType.nullableResolver must be implemented.")
10-
open fun nonNullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String = throw NotImplementedError("MyResolverType.nonNullableResolver must be implemented.")
6+
open class MyIncludedResolverType {
7+
open fun nullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = throw NotImplementedError("MyIncludedResolverType.nullableField must be implemented.")
8+
open fun nonNullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String = throw NotImplementedError("MyIncludedResolverType.nonNullableField must be implemented.")
9+
open fun nullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = throw NotImplementedError("MyIncludedResolverType.nullableResolver must be implemented.")
10+
open fun nonNullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String = throw NotImplementedError("MyIncludedResolverType.nonNullableResolver must be implemented.")
11+
open fun nullableListResolver(arg1: Int?, arg2: Int, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): List<String?>? = throw NotImplementedError("MyIncludedResolverType.nullableListResolver must be implemented.")
12+
open fun nonNullableListResolver(arg1: Int, arg2: Int?, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): List<String> = throw NotImplementedError("MyIncludedResolverType.nonNullableListResolver must be implemented.")
1113
}
1214

1315
@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
@@ -16,6 +18,8 @@ open class MySuspendResolverType {
1618
open suspend fun nonNullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String = throw NotImplementedError("MySuspendResolverType.nonNullableField must be implemented.")
1719
open suspend fun nullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String? = throw NotImplementedError("MySuspendResolverType.nullableResolver must be implemented.")
1820
open suspend fun nonNullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String = throw NotImplementedError("MySuspendResolverType.nonNullableResolver must be implemented.")
21+
open suspend fun nullableListResolver(arg1: Int?, arg2: Int, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): List<String?>? = throw NotImplementedError("MySuspendResolverType.nullableListResolver must be implemented.")
22+
open suspend fun nonNullableListResolver(arg1: Int, arg2: Int?, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): List<String> = throw NotImplementedError("MySuspendResolverType.nonNullableListResolver must be implemented.")
1923
}
2024

2125
@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
@@ -24,6 +28,8 @@ open class MyCompletableFutureResolverType {
2428
open fun nonNullableField(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture<String> = throw NotImplementedError("MyCompletableFutureResolverType.nonNullableField must be implemented.")
2529
open fun nullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture<String?> = throw NotImplementedError("MyCompletableFutureResolverType.nullableResolver must be implemented.")
2630
open fun nonNullableResolver(arg: String, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture<String> = throw NotImplementedError("MyCompletableFutureResolverType.nonNullableResolver must be implemented.")
31+
open fun nullableListResolver(arg1: Int?, arg2: Int, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture<List<String?>?> = throw NotImplementedError("MyCompletableFutureResolverType.nullableListResolver must be implemented.")
32+
open fun nonNullableListResolver(arg1: Int, arg2: Int?, dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): java.util.concurrent.CompletableFuture<List<String>> = throw NotImplementedError("MyCompletableFutureResolverType.nonNullableListResolver must be implemented.")
2733
}
2834

2935
@GraphQLValidObjectLocations(locations = [GraphQLValidObjectLocations.Locations.OBJECT])
@@ -33,6 +39,10 @@ data class MyExcludedResolverType(
3339
)
3440

3541
interface MyIncludedInterface {
42+
fun field(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String?
43+
}
44+
45+
interface MyIncludedInterfaceSuspend {
3646
suspend fun field(dataFetchingEnvironment: graphql.schema.DataFetchingEnvironment): String?
3747
}
3848

test/unit/should_honor_resolverClasses_config/schema.graphql

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,28 @@
1-
type MyResolverType {
1+
type MyIncludedResolverType {
22
nullableField: String
33
nonNullableField: String!
44
nullableResolver(arg: String!): String
55
nonNullableResolver(arg: String!): String!
6+
nullableListResolver(arg1: Int, arg2: Int!): [String]
7+
nonNullableListResolver(arg1: Int!, arg2: Int): [String!]!
68
}
79

8-
type MyIncludedResolverType {
10+
type MySuspendResolverType {
911
nullableField: String
1012
nonNullableField: String!
13+
nullableResolver(arg: String!): String
14+
nonNullableResolver(arg: String!): String!
15+
nullableListResolver(arg1: Int, arg2: Int!): [String]
16+
nonNullableListResolver(arg1: Int!, arg2: Int): [String!]!
17+
}
18+
19+
type MyCompletableFutureResolverType {
20+
nullableField: String
21+
nonNullableField: String!
22+
nullableResolver(arg: String!): String
23+
nonNullableResolver(arg: String!): String!
24+
nullableListResolver(arg1: Int, arg2: Int!): [String]
25+
nonNullableListResolver(arg1: Int!, arg2: Int): [String!]!
1126
}
1227

1328
type MyExcludedResolverType {
@@ -19,6 +34,10 @@ interface MyIncludedInterface {
1934
field: String
2035
}
2136

37+
interface MyIncludedInterfaceSuspend {
38+
field: String
39+
}
40+
2241
interface MyExcludedInterface {
2342
field: String
2443
}

0 commit comments

Comments
 (0)