Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 71 additions & 70 deletions docs/codegen-options.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ public class GraphQLCodegenGradleTask extends DefaultTask implements GraphQLCode
private Set<String> typesAsInterfaces = new HashSet<>();
private Set<String> resolverArgumentAnnotations = new HashSet<>();
private Set<String> parametrizedResolverAnnotations = new HashSet<>();
private Set<String> fieldsWithDataFetcherResult = new HashSet<>();
private final RelayConfig relayConfig = new RelayConfig();


Expand Down Expand Up @@ -190,6 +191,8 @@ public void generate() throws Exception {
fieldsWithoutResolvers != null ? fieldsWithoutResolvers : new HashSet<>());
mappingConfig.setFieldsToExcludeFromGeneration(
fieldsToExcludeFromGeneration != null ? fieldsToExcludeFromGeneration : new HashSet<>());
mappingConfig.setFieldsWithDataFetcherResult(
fieldsWithDataFetcherResult != null ? fieldsWithDataFetcherResult : new HashSet<>());
mappingConfig.setTypesAsInterfaces(
typesAsInterfaces != null ? typesAsInterfaces : new HashSet<>());
mappingConfig.setResolverArgumentAnnotations(
Expand Down Expand Up @@ -801,6 +804,17 @@ public void setFieldsToExcludeFromGeneration(Set<String> fieldsToExcludeFromGene
this.fieldsToExcludeFromGeneration = fieldsToExcludeFromGeneration;
}

@Input
@Optional
@Override
public Set<String> getFieldsWithDataFetcherResult() {
return fieldsWithDataFetcherResult;
}

public void setFieldsWithDataFetcherResult(Set<String> fieldsWithDataFetcherResult) {
this.fieldsWithDataFetcherResult = fieldsWithDataFetcherResult;
}

@Input
@Optional
@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,9 @@ public class GraphQLCodegenMojo extends AbstractMojo implements GraphQLCodegenCo
@Parameter
private String[] fieldsToExcludeFromGeneration;

@Parameter
private String[] fieldsWithDataFetcherResult;

@Parameter
private RelayConfig relayConfig = new RelayConfig();

Expand Down Expand Up @@ -301,6 +304,7 @@ public void execute() throws MojoExecutionException {
mappingConfig.setFieldsWithResolvers(mapToHashSet(fieldsWithResolvers));
mappingConfig.setFieldsWithoutResolvers(mapToHashSet(fieldsWithoutResolvers));
mappingConfig.setFieldsToExcludeFromGeneration(mapToHashSet(fieldsToExcludeFromGeneration));
mappingConfig.setFieldsWithDataFetcherResult(mapToHashSet(fieldsWithDataFetcherResult));
mappingConfig.setRelayConfig(relayConfig);

mappingConfig.setGenerateClient(generateClient);
Expand Down Expand Up @@ -618,6 +622,11 @@ public Set<String> getFieldsToExcludeFromGeneration() {
return mapToHashSet(fieldsToExcludeFromGeneration);
}

@Override
public Set<String> getFieldsWithDataFetcherResult() {
return mapToHashSet(fieldsWithDataFetcherResult);
}

@Override
public Boolean getGenerateAllMethodInProjection() {
return generateAllMethodInProjection;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,15 @@
import com.kobylynskyi.graphql.codegen.model.NamedDefinition;
import com.kobylynskyi.graphql.codegen.model.graphql.GraphQLOperation;
import com.kobylynskyi.graphql.codegen.utils.Utils;
import graphql.language.Directive;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

import static java.util.Arrays.asList;

Expand Down Expand Up @@ -102,7 +105,7 @@ public boolean isPrimitive(String possiblyPrimitiveType) {

@Override
public NamedDefinition getLanguageType(MappingContext mappingContext, String graphQLType, String name,
String parentTypeName, boolean mandatory, boolean collection) {
String parentTypeName, boolean mandatory, boolean collection, List<Directive> directives) {
Map<String, String> customTypesMapping = mappingContext.getCustomTypesMapping();
Set<String> serializeFieldsUsingObjectMapper = mappingContext.getUseObjectMapperForRequestSerialization();
String langTypeName;
Expand All @@ -116,6 +119,15 @@ public NamedDefinition getLanguageType(MappingContext mappingContext, String gra
} else {
langTypeName = DataModelMapper.getModelClassNameWithPrefixAndSuffix(mappingContext, graphQLType);
}

Set<String> fieldsWithDataFetcherResult = mappingContext.getFieldsWithDataFetcherResult();
Set<String> directivesNames = directives.stream()
.map(directive -> "@" + directive.getName())
.collect(Collectors.toSet());
if (directivesNames.stream().anyMatch(fieldsWithDataFetcherResult::contains)) {
langTypeName = wrapWithDataFetcherResult(langTypeName);
}

if (serializeFieldsUsingObjectMapper.contains(graphQLType) ||
(name != null && parentTypeName != null &&
serializeFieldsUsingObjectMapper.contains(parentTypeName + "." + name))) {
Expand All @@ -126,4 +138,7 @@ public NamedDefinition getLanguageType(MappingContext mappingContext, String gra
mandatory, primitiveCanBeUsed, serializeUsingObjectMapper);
}

private String wrapWithDataFetcherResult(String typeName) {
return "graphql.execution.DataFetcherResult<" + typeName + ">";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ private ParameterDefinition mapField(MappingContext mappingContext,
ExtendedFieldDefinition fieldDef,
ExtendedDefinition<?, ?> parentDefinition) {
NamedDefinition namedDefinition = graphQLTypeMapper
.getLanguageType(mappingContext, fieldDef.getType(), fieldDef.getName(), parentDefinition.getName());
.getLanguageType(mappingContext, fieldDef.getType(), fieldDef.getName(), parentDefinition.getName(), fieldDef.getDirectives());

ParameterDefinition parameter = new ParameterDefinition();
parameter.setName(dataModelMapper.capitalizeIfRestricted(mappingContext, fieldDef.getName()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.kobylynskyi.graphql.codegen.model.NamedDefinition;
import com.kobylynskyi.graphql.codegen.model.RelayConfig;
import com.kobylynskyi.graphql.codegen.model.definitions.ExtendedFieldDefinition;
import graphql.com.google.common.collect.ImmutableList;
import graphql.language.Argument;
import graphql.language.Directive;
import graphql.language.DirectivesContainer;
Expand Down Expand Up @@ -161,6 +162,21 @@ public NamedDefinition getLanguageType(MappingContext mappingContext, Type<?> gr
return getLanguageType(mappingContext, graphqlType, name, parentTypeName, false, false);
}

/**
* Convert GraphQL type to a corresponding language-specific type (java/scala/kotlin/etc)
*
* @param mappingContext Global mapping context
* @param graphqlType GraphQL type
* @param name GraphQL type name
* @param parentTypeName Name of the parent type
* @param directives GraphQL field directives
* @return Corresponding language-specific type (java/scala/kotlin/etc)
*/
public NamedDefinition getLanguageType(MappingContext mappingContext, Type<?> graphqlType, String name,
String parentTypeName, List<Directive> directives) {
return getLanguageType(mappingContext, graphqlType, name, parentTypeName, false, false, directives);
}

/**
* Convert GraphQL type to a corresponding language-specific type (java/scala/kotlin/etc)
*
Expand All @@ -175,12 +191,30 @@ public NamedDefinition getLanguageType(MappingContext mappingContext, Type<?> gr
public NamedDefinition getLanguageType(MappingContext mappingContext, Type<?> graphqlType,
String name, String parentTypeName,
boolean mandatory, boolean collection) {
return getLanguageType(mappingContext, graphqlType, name, parentTypeName, mandatory, collection, Collections.emptyList());
}

/**
* Convert GraphQL type to a corresponding language-specific type (java/scala/kotlin/etc)
*
* @param mappingContext Global mapping context
* @param graphqlType GraphQL type
* @param name GraphQL type name
* @param parentTypeName Name of the parent type
* @param mandatory GraphQL type is non-null
* @param collection GraphQL type is collection
* @param directives GraphQL field directives
* @return Corresponding language-specific type (java/scala/kotlin/etc)
*/
public NamedDefinition getLanguageType(MappingContext mappingContext, Type<?> graphqlType,
String name, String parentTypeName,
boolean mandatory, boolean collection, List<Directive> directives) {
if (graphqlType instanceof TypeName) {
return getLanguageType(mappingContext, ((TypeName) graphqlType).getName(), name, parentTypeName, mandatory,
collection);
collection, directives);
} else if (graphqlType instanceof ListType) {
NamedDefinition mappedCollectionType = getLanguageType(mappingContext, ((ListType) graphqlType).getType(),
name, parentTypeName, false, true);
name, parentTypeName, false, true, directives);
if (mappedCollectionType.isInterfaceOrUnion() &&
isInterfaceOrUnion(mappingContext, parentTypeName)) {
mappedCollectionType.setJavaName(
Expand All @@ -192,7 +226,7 @@ public NamedDefinition getLanguageType(MappingContext mappingContext, Type<?> gr
return mappedCollectionType;
} else if (graphqlType instanceof NonNullType) {
return getLanguageType(mappingContext, ((NonNullType) graphqlType).getType(), name, parentTypeName, true,
collection);
collection, directives);
}
throw new IllegalArgumentException("Unknown type: " + graphqlType);
}
Expand All @@ -206,10 +240,12 @@ public NamedDefinition getLanguageType(MappingContext mappingContext, Type<?> gr
* @param parentTypeName Name of the parent type
* @param mandatory GraphQL type is non-null
* @param collection GraphQL type is collection
* @param directives GraphQL field directives
* @return Corresponding language-specific type (java/scala/kotlin/etc)
*/
public NamedDefinition getLanguageType(MappingContext mappingContext, String graphQLType, String name,
String parentTypeName, boolean mandatory, boolean collection) {
String parentTypeName, boolean mandatory, boolean collection,
List<Directive> directives) {
Map<String, String> customTypesMapping = mappingContext.getCustomTypesMapping();
Set<String> serializeFieldsUsingObjectMapper = mappingContext.getUseObjectMapperForRequestSerialization();
String langTypeName;
Expand Down Expand Up @@ -273,5 +309,4 @@ protected boolean isInterfaceOrUnion(MappingContext mappingContext, String graph
return mappingContext.getInterfacesName().contains(graphQLType) ||
mappingContext.getUnionsNames().contains(graphQLType);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,21 @@ public interface GraphQLCodegenConfiguration {
*/
Set<String> getFieldsToExcludeFromGeneration();

/**
* Fields that require DataFetcherResult.
*
* <p>Values should be defined here in format: TypeName, TypeName.fieldName, @directive
*
*
* <p>E.g.:
* <ul>
* <li>{@code @dataFetcherResult}</li>
* </ul>
*
* @return Set of types and fields that should have DataFetcherResult.
*/
Set<String> getFieldsWithDataFetcherResult();

/**
* Specifies whether return types of generated API interface should be wrapped into <code>java.util.Optional</code>
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ public class MappingConfig implements GraphQLCodegenConfiguration, Combinable<Ma
private Set<String> fieldsWithResolvers = new HashSet<>();
private Set<String> fieldsWithoutResolvers = new HashSet<>();
private Set<String> fieldsToExcludeFromGeneration = new HashSet<>();
private Set<String> fieldsWithDataFetcherResult = new HashSet<>();

// parent interfaces configs:
private String queryResolverParentInterface;
Expand Down Expand Up @@ -192,6 +193,7 @@ public void combine(MappingConfig source) {
parametrizedInputSuffix = getValueOrDefaultToThis(source,
GraphQLCodegenConfiguration::getParametrizedInputSuffix);
fieldsWithResolvers = combineSet(fieldsWithResolvers, source.fieldsWithResolvers);
fieldsWithDataFetcherResult = combineSet(fieldsWithDataFetcherResult, source.fieldsWithDataFetcherResult);
fieldsWithoutResolvers = combineSet(fieldsWithoutResolvers, source.fieldsWithoutResolvers);
fieldsToExcludeFromGeneration = combineSet(fieldsToExcludeFromGeneration, source.fieldsToExcludeFromGeneration);
customTypesMapping = combineMap(customTypesMapping, source.customTypesMapping);
Expand Down Expand Up @@ -606,6 +608,15 @@ public Set<String> getFieldsToExcludeFromGeneration() {
return fieldsToExcludeFromGeneration;
}

public void setFieldsWithDataFetcherResult(Set<String> fieldsWithDataFetcherResult) {
this.fieldsWithDataFetcherResult = fieldsWithDataFetcherResult;
}

@Override
public Set<String> getFieldsWithDataFetcherResult() {
return fieldsWithDataFetcherResult;
}

public void setFieldsToExcludeFromGeneration(Set<String> fieldsToExcludeFromGeneration) {
this.fieldsToExcludeFromGeneration = fieldsToExcludeFromGeneration;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,11 @@ public Set<String> getFieldsToExcludeFromGeneration() {
return config.getFieldsToExcludeFromGeneration();
}

@Override
public Set<String> getFieldsWithDataFetcherResult() {
return config.getFieldsWithDataFetcherResult();
}

@Override
public Boolean getGenerateClient() {
return config.getGenerateClient();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.kobylynskyi.graphql.codegen;

import com.kobylynskyi.graphql.codegen.java.JavaGraphQLCodegen;
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.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;

import static com.kobylynskyi.graphql.codegen.TestUtils.getFileByName;
import static java.util.Arrays.asList;
import static java.util.Collections.singleton;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;

class GraphQLCodegenFieldsWithDataFetcherTest {

private final File outputBuildDir = new File("build/generated");
private final File outputJavaClassesDir = new File("build/generated/com/github/graphql");
private final MappingConfig mappingConfig = new MappingConfig();

@BeforeEach
void init() {
mappingConfig.setPackageName("com.github.graphql");
mappingConfig.setFieldsWithDataFetcherResult(new HashSet<>(singleton("@dataFetcherResult")));
}

@AfterEach
void cleanup() {
Utils.deleteDir(outputBuildDir);
}

@Test
void generate_fieldsWithDataFetcherResult() throws Exception {
mappingConfig.getFieldsWithDataFetcherResult().add("orders");
mappingConfig.getFieldsWithDataFetcherResult().add("cart");

generate("src/test/resources/schemas/fields-with-data-fetcher-result.graphqls");

File[] files = Objects.requireNonNull(outputJavaClassesDir.listFiles());
Assertions.assertNotNull(files);

List<String> generatedFileNames = Arrays.stream(files).map(File::getName).sorted().collect(toList());
assertEquals(
asList("Cart.java", "Order.java", "QueryResolver.java", "User.java", "UserCurrentQueryResolver.java"),
generatedFileNames
);

File user = getFileByName(files, "User.java");
String userContext = Utils.getFileContent(user.getPath()).trim();

assertTrue(userContext.contains("java.util.List<graphql.execution.DataFetcherResult<Order>>"));
assertTrue(userContext.contains("graphql.execution.DataFetcherResult<Cart>"));
}

private void generate(String o) throws IOException {
new JavaGraphQLCodegen(singletonList(o), outputBuildDir, mappingConfig,
TestUtils.getStaticGeneratedInfo(mappingConfig))
.generate();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# A GraphQL schema provides a root type for each kind of operation.
schema {
# The query root.
query: Query
}

type Query {
userCurrent: User
}

type User {
username: String!
email: String!
orders: [Order!]! @dataFetcherResult
cart: Cart! @dataFetcherResult
}

type Order {
number: String!
price: String!
}

type Cart {
id: Long!
}

directive @dataFetcherResult on FIELD_DEFINITION