diff --git a/docs/codegen-options.md b/docs/codegen-options.md
index f42e47dec..0a552a2a4 100644
--- a/docs/codegen-options.md
+++ b/docs/codegen-options.md
@@ -31,7 +31,7 @@
| `typeResolverPrefix` | String | Empty | Sets the prefix for GraphQL type resolver classes. |
| `typeResolverSuffix` | String | `Resolver` | Sets the suffix for GraphQL type resolver classes. |
| `customTypesMapping` | Map(String,String) | Empty | *See [CustomTypesMapping](#option-customtypesmapping)* |
-| `customTemplatesRoot` | File | Project's dir | Use to supply the path the to custom FreeMarker templates root directory. |
+| `customTemplatesRoot` | File | Project's dir | Use to supply the path the to custom FreeMarker templates root directory. |
| `customTemplates` | Map(String,String) | Empty | Use to supply paths to custom FreeMarker templates for code generation. |
| `customAnnotationsMapping` | Map(String,String[]) | Empty | *See [CustomAnnotationsMapping](#option-customannotationsmapping)* |
| `directiveAnnotationsMapping` | Map(String,String[]) | Empty | *See [DirectiveAnnotationsMapping](#option-directiveannotationsmapping)* |
@@ -45,6 +45,7 @@
| `generateModelsForRootTypes` | Boolean | False | Specifies whether model classes should be generated for `type Query`, `type Subscription`, `type Mutation`. |
| `useOptionalForNullableReturnTypes` | Boolean | False | Specifies whether nullable return types of api methods should be wrapped into [`java.util.Optional<>`](https://docs.oracle.com/javase/8/docs/api/index.html?java/util/Optional.html). Lists will not be wrapped. |
| `generateApisWithThrowsException` | Boolean | True | Specifies whether api interface methods should have `throws Exception` in signature. |
+| `generateApisWithSuspendFunctions` | Boolean | False | Specifies whether api interface methods should have `suspend` modifier in signature. Only supported in Kotlin. |
| `generateNoArgsConstructorOnly` | Boolean | False | Specifies whether model classes should only have a no-args constructor. All-args constructor will not be generated in case value is true |
| `generateModelsWithPublicFields` | Boolean | False | Specifies whether model classes should have public fields and NO getters/setters. By default, fields are private and there are getters/setters for each field. |
| `apiReturnType` | String | Empty | Return type for api methods (query/mutation). For example: `reactor.core.publisher.Mono`, etc. |
diff --git a/plugins/gradle/graphql-java-codegen-gradle-plugin/src/main/java/io/github/kobylynskyi/graphql/codegen/gradle/GraphQLCodegenGradleTask.java b/plugins/gradle/graphql-java-codegen-gradle-plugin/src/main/java/io/github/kobylynskyi/graphql/codegen/gradle/GraphQLCodegenGradleTask.java
index 89eb7f255..d7b958e2e 100644
--- a/plugins/gradle/graphql-java-codegen-gradle-plugin/src/main/java/io/github/kobylynskyi/graphql/codegen/gradle/GraphQLCodegenGradleTask.java
+++ b/plugins/gradle/graphql-java-codegen-gradle-plugin/src/main/java/io/github/kobylynskyi/graphql/codegen/gradle/GraphQLCodegenGradleTask.java
@@ -87,6 +87,8 @@ public class GraphQLCodegenGradleTask extends DefaultTask implements GraphQLCode
private Boolean generateModelsForRootTypes = MappingConfigConstants.DEFAULT_GENERATE_MODELS_FOR_ROOT_TYPES;
private Boolean useOptionalForNullableReturnTypes = MappingConfigConstants.DEFAULT_USE_OPTIONAL_FOR_NULLABLE_RETURN_TYPES;
private Boolean generateApisWithThrowsException = MappingConfigConstants.DEFAULT_GENERATE_APIS_WITH_THROWS_EXCEPTION;
+ private Boolean generateApisWithSuspendFunctions =
+ MappingConfigConstants.DEFAULT_GENERATE_APIS_WITH_SUSPEND_FUNCTIONS;
private Boolean generateJacksonTypeIdResolver = MappingConfigConstants.DEFAULT_GENERATE_JACKSON_TYPE_ID_RESOLVER;
private Boolean addGeneratedAnnotation = MappingConfigConstants.DEFAULT_ADD_GENERATED_ANNOTATION;
private Boolean generateNoArgsConstructorOnly = MappingConfigConstants.DEFAULT_GENERATE_NOARGS_CONSTRUCTOR_ONLY;
@@ -167,6 +169,7 @@ public void generate() throws Exception {
mappingConfig.setGenerateToString(generateToString);
mappingConfig.setUseOptionalForNullableReturnTypes(useOptionalForNullableReturnTypes);
mappingConfig.setGenerateApisWithThrowsException(generateApisWithThrowsException);
+ mappingConfig.setGenerateApisWithSuspendFunctions(generateApisWithSuspendFunctions);
mappingConfig.setGenerateJacksonTypeIdResolver(generateJacksonTypeIdResolver);
mappingConfig.setGenerateNoArgsConstructorOnly(generateNoArgsConstructorOnly);
mappingConfig.setGenerateModelsWithPublicFields(generateModelsWithPublicFields);
@@ -688,6 +691,17 @@ public void setGenerateApisWithThrowsException(Boolean generateApisWithThrowsExc
this.generateApisWithThrowsException = generateApisWithThrowsException;
}
+ @Input
+ @Optional
+ @Override
+ public Boolean getGenerateApisWithSuspendFunctions() {
+ return generateApisWithSuspendFunctions;
+ }
+
+ public void setGenerateApisWithSuspendFunctions(Boolean generateApisWithSuspendFunctions) {
+ this.generateApisWithSuspendFunctions = generateApisWithSuspendFunctions;
+ }
+
@Input
@Optional
@Override
diff --git a/plugins/maven/graphql-java-codegen-maven-plugin/src/main/java/io/github/kobylynskyi/graphql/codegen/GraphQLCodegenMojo.java b/plugins/maven/graphql-java-codegen-maven-plugin/src/main/java/io/github/kobylynskyi/graphql/codegen/GraphQLCodegenMojo.java
index 25eac92e4..2d7a8380a 100644
--- a/plugins/maven/graphql-java-codegen-maven-plugin/src/main/java/io/github/kobylynskyi/graphql/codegen/GraphQLCodegenMojo.java
+++ b/plugins/maven/graphql-java-codegen-maven-plugin/src/main/java/io/github/kobylynskyi/graphql/codegen/GraphQLCodegenMojo.java
@@ -155,6 +155,9 @@ public class GraphQLCodegenMojo extends AbstractMojo implements GraphQLCodegenCo
@Parameter(defaultValue = MappingConfigConstants.DEFAULT_GENERATE_APIS_WITH_THROWS_EXCEPTION_STRING)
private boolean generateApisWithThrowsException;
+ @Parameter(defaultValue = MappingConfigConstants.DEFAULT_GENERATE_APIS_WITH_SUSPEND_FUNCTIONS_STRING)
+ private boolean generateApisWithSuspendFunctions;
+
@Parameter(defaultValue = MappingConfigConstants.DEFAULT_GENERATE_JACKSON_TYPE_ID_RESOLVER_STRING)
private boolean generateJacksonTypeIdResolver;
@@ -285,6 +288,7 @@ public void execute() throws MojoExecutionException {
mappingConfig.setGenerateModelsForRootTypes(generateModelsForRootTypes);
mappingConfig.setUseOptionalForNullableReturnTypes(useOptionalForNullableReturnTypes);
mappingConfig.setGenerateApisWithThrowsException(generateApisWithThrowsException);
+ mappingConfig.setGenerateApisWithSuspendFunctions(generateApisWithSuspendFunctions);
mappingConfig.setGenerateJacksonTypeIdResolver(generateJacksonTypeIdResolver);
mappingConfig.setAddGeneratedAnnotation(addGeneratedAnnotation);
mappingConfig.setGeneratedAnnotation(generatedAnnotation);
@@ -550,6 +554,11 @@ public Boolean getGenerateApisWithThrowsException() {
return generateApisWithThrowsException;
}
+ @Override
+ public Boolean getGenerateApisWithSuspendFunctions() {
+ return generateApisWithSuspendFunctions;
+ }
+
@Override
public Boolean getGenerateJacksonTypeIdResolver() {
return generateJacksonTypeIdResolver;
diff --git a/src/main/java/com/kobylynskyi/graphql/codegen/mapper/FieldDefinitionsToResolverDataModelMapper.java b/src/main/java/com/kobylynskyi/graphql/codegen/mapper/FieldDefinitionsToResolverDataModelMapper.java
index ab33b9658..a8a0626f2 100644
--- a/src/main/java/com/kobylynskyi/graphql/codegen/mapper/FieldDefinitionsToResolverDataModelMapper.java
+++ b/src/main/java/com/kobylynskyi/graphql/codegen/mapper/FieldDefinitionsToResolverDataModelMapper.java
@@ -212,6 +212,7 @@ private OperationDefinition map(MappingContext mappingContext, ExtendedFieldDefi
operation.setJavaDoc(fieldDef.getJavaDoc());
operation.setDeprecated(DeprecatedDefinitionBuilder.build(mappingContext, fieldDef));
operation.setThrowsException(mappingContext.getGenerateApisWithThrowsException());
+ operation.setSuspend(mappingContext.getGenerateApisWithSuspendFunctions());
return operation;
}
diff --git a/src/main/java/com/kobylynskyi/graphql/codegen/model/GraphQLCodegenConfiguration.java b/src/main/java/com/kobylynskyi/graphql/codegen/model/GraphQLCodegenConfiguration.java
index 387fd0f09..dfb7db970 100644
--- a/src/main/java/com/kobylynskyi/graphql/codegen/model/GraphQLCodegenConfiguration.java
+++ b/src/main/java/com/kobylynskyi/graphql/codegen/model/GraphQLCodegenConfiguration.java
@@ -267,6 +267,13 @@ public interface GraphQLCodegenConfiguration {
*/
Boolean getGenerateApisWithThrowsException();
+ /**
+ * Whether signatures of API interface methods should have suspend
modifier.
+ *
+ * @return true if API interfaces methods signature should contain suspend
modifier.
+ */
+ Boolean getGenerateApisWithSuspendFunctions();
+
/**
* Specifies whether generated classes should be annotated with @Generated
*
diff --git a/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingConfig.java b/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingConfig.java
index 0ab5e3b22..dc29f4607 100644
--- a/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingConfig.java
+++ b/src/main/java/com/kobylynskyi/graphql/codegen/model/MappingConfig.java
@@ -53,6 +53,7 @@ public class MappingConfig implements GraphQLCodegenConfiguration, Combinable javaDoc = new ArrayList<>();
private DeprecatedDefinition deprecated;
private boolean throwsException;
+ private boolean suspend;
public String getName() {
return name;
@@ -90,4 +91,12 @@ public boolean isThrowsException() {
public void setThrowsException(boolean throwsException) {
this.throwsException = throwsException;
}
+
+ public boolean isSuspend() {
+ return suspend;
+ }
+
+ public void setSuspend(boolean suspend) {
+ this.suspend = suspend;
+ }
}
diff --git a/src/main/resources/templates/kotlin-lang/operations.ftl b/src/main/resources/templates/kotlin-lang/operations.ftl
index 859744daa..1802a3537 100755
--- a/src/main/resources/templates/kotlin-lang/operations.ftl
+++ b/src/main/resources/templates/kotlin-lang/operations.ftl
@@ -38,7 +38,7 @@ interface ${className}<#if implements?has_content> : <#list implements as interf
<#if operation.throwsException>
@Throws(Exception::class)
#if>
- fun ${operation.name}(<#list operation.parameters as param><#list param.annotations as paramAnnotation>@${paramAnnotation}<#if param.annotations?has_content> #if>#list>${param.name}: ${param.type}<#if param_has_next>, #if>#list>): ${operation.type}
+ <#if operation.suspend>suspend #if>fun ${operation.name}(<#list operation.parameters as param><#list param.annotations as paramAnnotation>@${paramAnnotation}<#if param.annotations?has_content> #if>#list>${param.name}: ${param.type}<#if param_has_next>, #if>#list>): ${operation.type}
#list>
}
diff --git a/src/test/java/com/kobylynskyi/graphql/codegen/kotlin/GraphQLCodegenSuspendTest.java b/src/test/java/com/kobylynskyi/graphql/codegen/kotlin/GraphQLCodegenSuspendTest.java
new file mode 100644
index 000000000..7f99e676e
--- /dev/null
+++ b/src/test/java/com/kobylynskyi/graphql/codegen/kotlin/GraphQLCodegenSuspendTest.java
@@ -0,0 +1,62 @@
+package com.kobylynskyi.graphql.codegen.kotlin;
+
+import com.kobylynskyi.graphql.codegen.MaxQueryTokensExtension;
+import com.kobylynskyi.graphql.codegen.TestUtils;
+import com.kobylynskyi.graphql.codegen.model.GeneratedLanguage;
+import com.kobylynskyi.graphql.codegen.model.MappingConfig;
+import com.kobylynskyi.graphql.codegen.utils.Utils;
+import org.junit.jupiter.api.AfterEach;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Objects;
+import java.util.Set;
+
+import static com.kobylynskyi.graphql.codegen.TestUtils.assertSameTrimmedContent;
+import static java.util.Arrays.asList;
+import static java.util.Collections.singletonList;
+import static java.util.stream.Collectors.toSet;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+@ExtendWith(MaxQueryTokensExtension.class)
+class GraphQLCodegenSuspendTest {
+
+ private final File outputBuildDir = new File("build/generated");
+ private final MappingConfig mappingConfig = new MappingConfig();
+
+ @BeforeEach
+ void init() {
+ mappingConfig.setGenerateApisWithSuspendFunctions(true);
+ mappingConfig.setGeneratedLanguage(GeneratedLanguage.KOTLIN);
+ }
+
+ @AfterEach
+ void cleanup() {
+ Utils.deleteDir(outputBuildDir);
+ }
+
+ @Test
+ void generate_ApiWithSuspendFunction() throws Exception {
+ new KotlinGraphQLCodegen(
+ singletonList("src/test/resources/schemas/kt/suspend.graphqls"),
+ outputBuildDir, mappingConfig, TestUtils.getStaticGeneratedInfo(mappingConfig)
+ ).generate();
+
+ File[] files = Objects.requireNonNull(outputBuildDir.listFiles());
+
+ Set generatedFileNames = Arrays.stream(files).map(File::getName).collect(toSet());
+ assertEquals(new HashSet<>(asList("FriendsQueryResolver.kt", "QueryResolver.kt", "Friend.kt")),
+ generatedFileNames);
+
+ for (File file : files) {
+ assertSameTrimmedContent(
+ new File(String.format("src/test/resources/expected-classes/kt/suspend/%s.txt", file.getName())),
+ file);
+ }
+ }
+
+}
diff --git a/src/test/resources/expected-classes/kt/suspend/Friend.kt.txt b/src/test/resources/expected-classes/kt/suspend/Friend.kt.txt
new file mode 100644
index 000000000..4fd887790
--- /dev/null
+++ b/src/test/resources/expected-classes/kt/suspend/Friend.kt.txt
@@ -0,0 +1,9 @@
+@javax.annotation.Generated(
+ value = ["com.kobylynskyi.graphql.codegen.GraphQLCodegen"],
+ date = "2020-12-31T23:59:59-0500"
+)
+data class Friend(
+ val name: String
+) {
+
+}
diff --git a/src/test/resources/expected-classes/kt/suspend/FriendsQueryResolver.kt.txt b/src/test/resources/expected-classes/kt/suspend/FriendsQueryResolver.kt.txt
new file mode 100644
index 000000000..4600a6f47
--- /dev/null
+++ b/src/test/resources/expected-classes/kt/suspend/FriendsQueryResolver.kt.txt
@@ -0,0 +1,10 @@
+@javax.annotation.Generated(
+ value = ["com.kobylynskyi.graphql.codegen.GraphQLCodegen"],
+ date = "2020-12-31T23:59:59-0500"
+)
+interface FriendsQueryResolver {
+
+ @Throws(Exception::class)
+ suspend fun friends(num: Int): List
+
+}
diff --git a/src/test/resources/expected-classes/kt/suspend/QueryResolver.kt.txt b/src/test/resources/expected-classes/kt/suspend/QueryResolver.kt.txt
new file mode 100644
index 000000000..1fe98c154
--- /dev/null
+++ b/src/test/resources/expected-classes/kt/suspend/QueryResolver.kt.txt
@@ -0,0 +1,10 @@
+@javax.annotation.Generated(
+ value = ["com.kobylynskyi.graphql.codegen.GraphQLCodegen"],
+ date = "2020-12-31T23:59:59-0500"
+)
+interface QueryResolver {
+
+ @Throws(Exception::class)
+ suspend fun friends(num: Int): List
+
+}
diff --git a/src/test/resources/schemas/kt/suspend.graphqls b/src/test/resources/schemas/kt/suspend.graphqls
new file mode 100644
index 000000000..7193c52e5
--- /dev/null
+++ b/src/test/resources/schemas/kt/suspend.graphqls
@@ -0,0 +1,7 @@
+type Query {
+ friends(num: Int!): [Friend!]!
+}
+
+type Friend {
+ name: String!
+}