Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

support placeholder at apiReturnListType and apiReturnType #1200

Merged
merged 8 commits into from
May 15, 2023
Merged
Show file tree
Hide file tree
Changes from 7 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
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,25 @@ 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);
matcher.find();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matsudamper I think it's better if you do if (matcher.find()) { ... }

String listElement = matcher.group(1);
return mappingContext.getApiReturnListType().replace(
MappingConfigConstants.API_RETURN_NAME_PLACEHOLDER,
listElement);
} 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,13 +2,16 @@

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;
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 java.util.Arrays.asList;

Expand All @@ -21,6 +24,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 @@ -92,11 +96,32 @@ 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);
matcher.find();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matsudamper I think it's better if you do 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 {
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,25 @@ 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);
matcher.find();
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@matsudamper I think it's better if you do if (matcher.find()) { ... }

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Implemented.
2542b8f

String listElement = matcher.group(1);
return mappingContext.getApiReturnListType().replace(
MappingConfigConstants.API_RETURN_NAME_PLACEHOLDER,
listElement);
} 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();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package com.kobylynskyi.graphql.codegen.kotlin;

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 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.KOTLIN);
}

@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 = getChildFunction(
"java.util.concurrent.CompletionStage<graphql.execution.DataFetcherResult<List<EventProperty?>?>>"
);
assertFileContainsElements(
files,
"EventPropertyResolver.kt",
requireChildText
);

String requireParentText = getParentFunction(
"java.util.concurrent.CompletionStage<graphql.execution.DataFetcherResult<Event?>>"
);
assertFileContainsElements(
files,
"EventPropertyResolver.kt",
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.kt",
getChildFunction(
"reactor.core.publisher.Mono<graphql.execution.DataFetcherResult<EventProperty?>>?"
)
);

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

private String getChildFunction(String returnType) {
return "fun child(eventProperty: EventProperty, first: Int?, last: Int?)" +
": " + returnType;
}

private String getParentFunction(String returnType) {
return "fun parent(eventProperty: EventProperty, withStatus: EventStatus?, createdAfter: String?)" +
": " + returnType;
}

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

}
Loading