Skip to content

Commit

Permalink
Support {TYPE} placeholder in apiReturnType and apiReturnListType #1167
Browse files Browse the repository at this point in the history
… (#1200)
  • Loading branch information
matsudamper authored May 15, 2023
1 parent a3e7551 commit 8d9d0de
Show file tree
Hide file tree
Showing 7 changed files with 408 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.kobylynskyi.graphql.codegen.mapper.DataModelMapper;
import com.kobylynskyi.graphql.codegen.mapper.GraphQLTypeMapper;
import com.kobylynskyi.graphql.codegen.model.MappingConfigConstants;
import com.kobylynskyi.graphql.codegen.model.MappingContext;
import com.kobylynskyi.graphql.codegen.model.NamedDefinition;
import com.kobylynskyi.graphql.codegen.model.graphql.GraphQLOperation;
Expand All @@ -10,6 +11,8 @@
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.Arrays.asList;

Expand All @@ -19,6 +22,7 @@
public class JavaGraphQLTypeMapper extends GraphQLTypeMapper {

public static final String JAVA_UTIL_LIST = "java.util.List";
public static final Pattern JAVA_UTIL_LIST_ELEMENT_REGEX = Pattern.compile("java\\.util\\.List<(.+)>");
private static final String JAVA_UTIL_OPTIONAL = "java.util.Optional";
private static final Set<String> JAVA_PRIMITIVE_TYPES = new HashSet<>(asList(
"byte", "short", "int", "long", "float", "double", "char", "boolean"));
Expand Down Expand Up @@ -65,11 +69,28 @@ public String wrapApiReturnTypeIfRequired(MappingContext mappingContext,
if (computedTypeName.startsWith(JAVA_UTIL_LIST) &&
Utils.isNotBlank(mappingContext.getApiReturnListType())) {
// in case it is query/mutation, return type is list and apiReturnListType is set
return computedTypeName.replace(JAVA_UTIL_LIST, mappingContext.getApiReturnListType());
if (mappingContext.getApiReturnListType().contains(MappingConfigConstants.API_RETURN_NAME_PLACEHOLDER)) {
Matcher matcher = JAVA_UTIL_LIST_ELEMENT_REGEX.matcher(computedTypeName);
if (matcher.find()) {
String listElement = matcher.group(1);
return mappingContext.getApiReturnListType().replace(
MappingConfigConstants.API_RETURN_NAME_PLACEHOLDER,
listElement);
} else {
throw new IllegalStateException();
}
} else {
return computedTypeName.replace(JAVA_UTIL_LIST, mappingContext.getApiReturnListType());
}
}
if (Utils.isNotBlank(mappingContext.getApiReturnType())) {
// in case it is query/mutation and apiReturnType is set
return getGenericsString(mappingContext, mappingContext.getApiReturnType(), computedTypeName);
if (mappingContext.getApiReturnType().contains(MappingConfigConstants.API_RETURN_NAME_PLACEHOLDER)) {
return mappingContext.getApiReturnType()
.replace(MappingConfigConstants.API_RETURN_NAME_PLACEHOLDER, computedTypeName);
} else {
return getGenericsString(mappingContext, mappingContext.getApiReturnType(), computedTypeName);
}
}
return getTypeConsideringPrimitive(mappingContext, namedDefinition, computedTypeName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.kobylynskyi.graphql.codegen.mapper.DataModelMapper;
import com.kobylynskyi.graphql.codegen.mapper.GraphQLTypeMapper;
import com.kobylynskyi.graphql.codegen.model.MappingConfigConstants;
import com.kobylynskyi.graphql.codegen.model.MappingContext;
import com.kobylynskyi.graphql.codegen.model.NamedDefinition;
import com.kobylynskyi.graphql.codegen.model.definitions.ExtendedFieldDefinition;
Expand All @@ -10,6 +11,8 @@

import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static java.util.Arrays.asList;

Expand All @@ -22,6 +25,7 @@
public class KotlinGraphQLTypeMapper extends GraphQLTypeMapper {

private static final String KOTLIN_UTIL_LIST = "List";
public static final Pattern KOTLIN_UTIL_LIST_ELEMENT_REGEX = Pattern.compile("List<(.+)>");
private static final String KOTLIN_UTIL_NULLABLE = "?";
// Char Boolean are not primitive type, but non null equivalent jvm primitive types.
private static final Set<String> KOTLIN_PRIMITIVE_TYPES = new HashSet<>(
Expand Down Expand Up @@ -93,11 +97,35 @@ public String wrapApiReturnTypeIfRequired(MappingContext mappingContext,
if (computedTypeName.startsWith(KOTLIN_UTIL_LIST) &&
Utils.isNotBlank(mappingContext.getApiReturnListType())) {
// in case it is query/mutation, return type is list and apiReturnListType is set
return computedTypeName.replace(KOTLIN_UTIL_LIST, mappingContext.getApiReturnListType());
if (mappingContext.getApiReturnListType().contains(MappingConfigConstants.API_RETURN_NAME_PLACEHOLDER)) {
boolean isNullable = computedTypeName.endsWith(KOTLIN_UTIL_NULLABLE);

Matcher matcher = KOTLIN_UTIL_LIST_ELEMENT_REGEX.matcher(computedTypeName);
if (matcher.find()) {
String listElement = matcher.group(1);
computedTypeName = mappingContext.getApiReturnListType()
.replace(MappingConfigConstants.API_RETURN_NAME_PLACEHOLDER, listElement);

if (isNullable) {
return computedTypeName + "?";
} else {
return computedTypeName;
}
} else {
throw new IllegalStateException();
}
} else {
return computedTypeName.replace(KOTLIN_UTIL_LIST, mappingContext.getApiReturnListType());
}
}
if (Utils.isNotBlank(mappingContext.getApiReturnType())) {
// in case it is query/mutation and apiReturnType is set
return getGenericsString(mappingContext, mappingContext.getApiReturnType(), computedTypeName);
if (mappingContext.getApiReturnType().contains(MappingConfigConstants.API_RETURN_NAME_PLACEHOLDER)) {
return mappingContext.getApiReturnType()
.replace(MappingConfigConstants.API_RETURN_NAME_PLACEHOLDER, computedTypeName);
} else {
return getGenericsString(mappingContext, mappingContext.getApiReturnType(), computedTypeName);
}
}
return getTypeConsideringPrimitive(mappingContext, namedDefinition, computedTypeName);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ public class MappingConfigConstants {
public static final String DEFAULT_VALIDATION_ANNOTATION = "javax.validation.constraints.NotNull";
public static final String PARENT_INTERFACE_TYPE_PLACEHOLDER = "{{TYPE}}";
public static final String TYPE_NAME_PLACEHOLDER = "{{TYPE_NAME}}";
public static final String API_RETURN_NAME_PLACEHOLDER = "{{TYPE}}";

public static final boolean DEFAULT_GENERATE_APIS = true;
public static final String DEFAULT_GENERATE_APIS_STRING = "true";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package com.kobylynskyi.graphql.codegen.scala;

import com.kobylynskyi.graphql.codegen.mapper.GraphQLTypeMapper;
import com.kobylynskyi.graphql.codegen.model.MappingConfigConstants;
import com.kobylynskyi.graphql.codegen.model.MappingContext;
import com.kobylynskyi.graphql.codegen.model.NamedDefinition;
import com.kobylynskyi.graphql.codegen.model.graphql.GraphQLOperation;
import com.kobylynskyi.graphql.codegen.utils.Utils;

import java.util.HashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.kobylynskyi.graphql.codegen.java.JavaGraphQLTypeMapper.JAVA_UTIL_LIST;
import static java.util.Arrays.asList;
Expand All @@ -18,6 +21,7 @@
public class ScalaGraphQLTypeMapper extends GraphQLTypeMapper {

private static final String SCALA_UTIL_LIST = "scala.Seq";
private static final Pattern SCALA_UTIL_LIST_ELEMENT_REGEX = Pattern.compile("scala\\.Seq\\[(.+)]");
private static final String SCALA_UTIL_OPTIONAL = "scala.Option";
private static final Set<String> SCALA_PRIMITIVE_TYPES = new HashSet<>(asList(
"Byte", "Short", "Int", "Long", "Float", "Double", "Char", "Boolean"));
Expand Down Expand Up @@ -72,11 +76,28 @@ public String wrapApiReturnTypeIfRequired(MappingContext mappingContext,
if (computedTypeName.startsWith(SCALA_UTIL_LIST) &&
Utils.isNotBlank(mappingContext.getApiReturnListType())) {
// in case it is query/mutation, return type is list and apiReturnListType is set
return computedTypeName.replace(SCALA_UTIL_LIST, mappingContext.getApiReturnListType());
if (mappingContext.getApiReturnListType().contains(MappingConfigConstants.API_RETURN_NAME_PLACEHOLDER)) {
Matcher matcher = SCALA_UTIL_LIST_ELEMENT_REGEX.matcher(computedTypeName);
if (matcher.find()) {
String listElement = matcher.group(1);
return mappingContext.getApiReturnListType().replace(
MappingConfigConstants.API_RETURN_NAME_PLACEHOLDER,
listElement);
} else {
throw new IllegalStateException();
}
} else {
return computedTypeName.replace(SCALA_UTIL_LIST, mappingContext.getApiReturnListType());
}
}
if (Utils.isNotBlank(mappingContext.getApiReturnType())) {
// in case it is query/mutation and apiReturnType is set
return getGenericsString(mappingContext, mappingContext.getApiReturnType(), computedTypeName);
if (mappingContext.getApiReturnType().contains(MappingConfigConstants.API_RETURN_NAME_PLACEHOLDER)) {
return mappingContext.getApiReturnType()
.replace(MappingConfigConstants.API_RETURN_NAME_PLACEHOLDER, computedTypeName);
} else {
return getGenericsString(mappingContext, mappingContext.getApiReturnType(), computedTypeName);
}
}
return getTypeConsideringPrimitive(mappingContext, namedDefinition, computedTypeName);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package com.kobylynskyi.graphql.codegen;

import com.kobylynskyi.graphql.codegen.java.JavaGraphQLCodegen;
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 java.io.File;
import java.io.IOException;
import java.util.Objects;

import static com.kobylynskyi.graphql.codegen.TestUtils.assertFileContainsElements;
import static java.util.Collections.singletonList;

class GraphQLCodegenApiReturnTypeTest {

private final File outputBuildDir = new File("build/generated");
private final File outputJavaClassesDir = new File("build/generated/com/kobylynskyi/graphql/test1");

private MappingConfig mappingConfig;

@BeforeEach
void init() {
mappingConfig = new MappingConfig();
mappingConfig.setPackageName("com.kobylynskyi.graphql.test1");
mappingConfig.setGeneratedLanguage(GeneratedLanguage.JAVA);
}

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

@Test
void generate_ApiReturnType_WithPlaceHolder() throws Exception {
mappingConfig.setApiReturnType(
"java.util.concurrent.CompletionStage<graphql.execution.DataFetcherResult<{{TYPE}}>>"
);

generate("src/test/resources/schemas/test.graphqls");

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

String requireChildText = getChildText(
"java.util.concurrent.CompletionStage<graphql.execution.DataFetcherResult" +
"<java.util.List<EventProperty>>>"
);
assertFileContainsElements(
files,
"EventPropertyResolver.java",
requireChildText
);

String requireParentText = getParentText(
"java.util.concurrent.CompletionStage<graphql.execution.DataFetcherResult<Event>>"
);
assertFileContainsElements(files, "EventPropertyResolver.java",
requireParentText
);
}

@Test
void generate_ApiReturnType_And_ApiReturnListType_WithPlaceHolder() throws Exception {
mappingConfig.setApiReturnType(
"java.util.concurrent.CompletionStage<graphql.execution.DataFetcherResult<{{TYPE}}>>"
);
mappingConfig.setApiReturnListType(
"reactor.core.publisher.Mono<graphql.execution.DataFetcherResult<{{TYPE}}>>"
);

generate("src/test/resources/schemas/test.graphqls");

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

assertFileContainsElements(
files,
"EventPropertyResolver.java",
getChildText(
"reactor.core.publisher.Mono<graphql.execution.DataFetcherResult<EventProperty>>"
)
);

assertFileContainsElements(
files,
"EventPropertyResolver.java",
getParentText(
"java.util.concurrent.CompletionStage<graphql.execution.DataFetcherResult<Event>>"
)
);
}

private String getChildText(String returnType) {
return returnType + " child(EventProperty eventProperty, Integer first, Integer last) throws Exception;";
}

private String getParentText(String returnType) {
return returnType +
" parent(EventProperty eventProperty, EventStatus withStatus, String createdAfter) throws Exception;";
}

private void generate(String path) throws IOException {
new JavaGraphQLCodegen(singletonList(path), outputBuildDir, mappingConfig,
TestUtils.getStaticGeneratedInfo(mappingConfig)).generate();
}

}
Loading

0 comments on commit 8d9d0de

Please sign in to comment.