Skip to content

Commit

Permalink
Add option to render Kotlin functions with suspend modifier
Browse files Browse the repository at this point in the history
  • Loading branch information
thevietto committed Nov 6, 2023
1 parent b13e059 commit c247463
Show file tree
Hide file tree
Showing 16 changed files with 165 additions and 2 deletions.
3 changes: 2 additions & 1 deletion docs/codegen-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)* |
Expand All @@ -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 <b>true</b> |
| `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. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -550,6 +554,11 @@ public Boolean getGenerateApisWithThrowsException() {
return generateApisWithThrowsException;
}

@Override
public Boolean getGenerateApisWithSuspendFunctions() {
return generateApisWithSuspendFunctions;
}

@Override
public Boolean getGenerateJacksonTypeIdResolver() {
return generateJacksonTypeIdResolver;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,13 @@ public interface GraphQLCodegenConfiguration {
*/
Boolean getGenerateApisWithThrowsException();

/**
* Whether signatures of API interface methods should have <code>suspend</code> modifier.
*
* @return <b>true</b> if API interfaces methods signature should contain <code>suspend</code> modifier.
*/
Boolean getGenerateApisWithSuspendFunctions();

/**
* Specifies whether generated classes should be annotated with @Generated
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ public class MappingConfig implements GraphQLCodegenConfiguration, Combinable<Ma
private Boolean generateModelsForRootTypes;
private Boolean useOptionalForNullableReturnTypes;
private Boolean generateApisWithThrowsException;
private Boolean generateApisWithSuspendFunctions;
private Boolean addGeneratedAnnotation;
private Boolean generateJacksonTypeIdResolver;
private Boolean supportUnknownFields;
Expand Down Expand Up @@ -168,6 +169,8 @@ public void combine(MappingConfig source) {
GraphQLCodegenConfiguration::getUseOptionalForNullableReturnTypes);
generateApisWithThrowsException = getValueOrDefaultToThis(source,
GraphQLCodegenConfiguration::getGenerateApisWithThrowsException);
generateApisWithSuspendFunctions = getValueOrDefaultToThis(source,
GraphQLCodegenConfiguration::getGenerateApisWithSuspendFunctions);
addGeneratedAnnotation = getValueOrDefaultToThis(source,
GraphQLCodegenConfiguration::getAddGeneratedAnnotation);
generateJacksonTypeIdResolver = getValueOrDefaultToThis(source,
Expand Down Expand Up @@ -526,6 +529,15 @@ public void setGenerateApisWithThrowsException(Boolean generateApisWithThrowsExc
this.generateApisWithThrowsException = generateApisWithThrowsException;
}

@Override
public Boolean getGenerateApisWithSuspendFunctions() {
return generateApisWithSuspendFunctions;
}

public void setGenerateApisWithSuspendFunctions(Boolean generateApisWithSuspendFunctions) {
this.generateApisWithSuspendFunctions = generateApisWithSuspendFunctions;
}

@Override
public Boolean getAddGeneratedAnnotation() {
return addGeneratedAnnotation;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ public class MappingConfigConstants {
public static final boolean DEFAULT_GENERATE_APIS_WITH_THROWS_EXCEPTION = true;
public static final String DEFAULT_GENERATE_APIS_WITH_THROWS_EXCEPTION_STRING = "true";

public static final boolean DEFAULT_GENERATE_APIS_WITH_SUSPEND_FUNCTIONS = false;
public static final String DEFAULT_GENERATE_APIS_WITH_SUSPEND_FUNCTIONS_STRING = "false";

public static final boolean DEFAULT_ADD_GENERATED_ANNOTATION = true;
public static final String DEFAULT_ADD_GENERATED_ANNOTATION_STRING = "true";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ public static void initDefaultValues(MappingConfig mappingConfig) {
mappingConfig.setGenerateApisWithThrowsException(
MappingConfigConstants.DEFAULT_GENERATE_APIS_WITH_THROWS_EXCEPTION);
}
if (mappingConfig.getGenerateApisWithSuspendFunctions() == null) {
mappingConfig.setGenerateApisWithSuspendFunctions(
MappingConfigConstants.DEFAULT_GENERATE_APIS_WITH_SUSPEND_FUNCTIONS);
}
if (mappingConfig.getAddGeneratedAnnotation() == null) {
mappingConfig.setAddGeneratedAnnotation(MappingConfigConstants.DEFAULT_ADD_GENERATED_ANNOTATION);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,11 @@ public Boolean getGenerateApisWithThrowsException() {
return config.getGenerateApisWithThrowsException();
}

@Override
public Boolean getGenerateApisWithSuspendFunctions() {
return config.getGenerateApisWithSuspendFunctions();
}

@Override
public Boolean getAddGeneratedAnnotation() {
return config.getAddGeneratedAnnotation();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ public class OperationDefinition {
private List<String> javaDoc = new ArrayList<>();
private DeprecatedDefinition deprecated;
private boolean throwsException;
private boolean suspend;

public String getName() {
return name;
Expand Down Expand Up @@ -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;
}
}
2 changes: 1 addition & 1 deletion src/main/resources/templates/kotlin-lang/operations.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -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>
}
Original file line number Diff line number Diff line change
@@ -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<String> 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);
}
}

}
Loading

0 comments on commit c247463

Please sign in to comment.