Conversation
| is IntValue -> CodeBlock.of("\$L", value.value) | ||
| is StringValue -> CodeBlock.of("\$S", value.value) | ||
| is StringValue -> { | ||
| // Only generate string literal default values for string types to prevent invalid Java initialization |
There was a problem hiding this comment.
Nit: I know we aligned on this, but I think we can do a bit better with a method to go through some common scalar types as well as primitives from strings. Anything we don't cover can just be null. So the call for stringValue can call this if it's not a string type to make a more best effort?
Something like....
private fun parseStringToType(value: String, type: JavaTypeName): CodeBlock? {
return when (type) {
// primitives -> String
ClassName.get(Int::class.java) -> CodeBlock.of("\$T.valueOf(\$S)", Integer::class.java, value)
ClassName.get(Long::class.java) -> CodeBlock.of("\$T.valueOf(\$S)", java.lang.Long::class.java, value)
ClassName.get(Float::class.java) -> CodeBlock.of("\$T.valueOf(\$S)", java.lang.Float::class.java, value)
ClassName.get(Double::class.java) -> CodeBlock.of("\$T.valueOf(\$S)", java.lang.Double::class.java, value)
ClassName.get(Boolean::class.java) -> CodeBlock.of("\$T.valueOf(\$S)", java.lang.Boolean::class.java, value)
// Example for custom scalar
ClassName.get(Uri::class.java) -> CodeBlock.of("\$T.parse(\$S)", Uri::class.java, value)
// Add more custom scalar handlers here
else -> null // fallback or throw error
}
}
There was a problem hiding this comment.
I like that idea and it would definitely make generated types easier to work with. I think supporting primitives would be simple enough but I'm curious about which other Java types you think should be automatically handled (java.net.Uri, java.math.BigDecimal, etc. would probably be commonly mapped to). I also wonder if this might make it inconsistent if only some default types are being generated.
There was a problem hiding this comment.
Ultimately, the list of possible types that would find their way here would be only GraphQL scalar types (Int, Float, String, Boolean) and custom scalar types right? So while we could have BigDecimal registered as a custom schema type, it would have to be plugged in?
Perhaps we should just start with default scalar types and log an enhancement to consider a way to support default code blocks for custom types? I think there could be other ways to plug in initializing custom types so we may find that trying to deduce it while generating the code may not be the final solution anyway.
There was a problem hiding this comment.
I think I'm thinking about it the other way around because the Java types are generated from the typeMapping the user provides. Looking at DataTypeGenerator I did see there's this block that checks for a few common Java types which does what Isaiah mentioned, so adding URI here would extend that flexibility.
private fun generateCode(
value: Value<out Value<*>>,
type: JavaTypeName,
inputTypeDefinitions: List<InputObjectTypeDefinition>,
): CodeBlock? =
when (type) {
BIG_DECIMAL -> bigDecimalCodeBlock(value, type)
CURRENCY -> currencyCodeBlock(value, type)
LOCALE -> localeCodeBlock(value, type)
ClassName.LONG.box() -> longCodeBlock(value, type)
else -> defaultCodeBlock(value, type, inputTypeDefinitions)
}
There was a problem hiding this comment.
Ok, we might be talking about more than one thing then. I think I'd just be in favor of applying the fix you have here and then logging a ticket to investigate refactoring the mappings to be more robust and support more cases, including Uri (for example).
There was a problem hiding this comment.
Sounds good! Agree that I would want to look more into how to support URI (using URI.create() vs. new URI() where one wraps the exception of malformed URIs and the other doesn't), thanks both for your input!
There was a problem hiding this comment.
I'm a bit late to the party since this was already merged, but what do folks think about handling URI as a special case? On the one hand we should not, because why handle this one type specially, but on the other hand, it's a very common case.
Something like this works:
is StringValue -> {
// Only generate string literal default values for string types to prevent invalid Java initialization
if (type is ClassName && type == ClassName.get(String::class.java)) {
CodeBlock.of("\$S", value.value)
} else if(type == ClassName.get(URI::class.java)) {
CodeBlock.of("URI.create(\$S)", value.value)
} else {
null
}
}
There was a problem hiding this comment.
I think this makes sense, I can add it in a follow-up PR.
iparadiso
left a comment
There was a problem hiding this comment.
The current impl looks fine to unblock breaking code. I left some thoughts on making a best case effort to parse more strings to complex types we could support in this PR in another one if we want.
a59e94d to
21c6b05
Compare
Closes #883
Changes
Currently, Codegen generates String literal default values in input types and assigns them to non-String Java types if such a mapping is defined (i.e. GraphQL
Uritojava.net.URI), which causes the generated Java class to not compile. This PR checks whether a String value in GraphQL is being assigned to a String type in Java and only generates the default value if the type and value are both String.The default value set in the schema is propagated by DGS at runtime so query/mutation behavior remains the same. This change just prevents generating code that does not compile.