-
Notifications
You must be signed in to change notification settings - Fork 700
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
Default argument for object with no required fields fails to build in Xcode #3249
Comments
Thanks so much for the bug report! We will definitely need to address this for the next release. We have a release slated to go out tomorrow, so this fix won't make it into that one, but we'll get to this ASAP. There is some complex nuances about default argument handling, so we're going to need to discuss what the actual expected behavior here is. But once we have that defined, I don't anticipate it will be much work to write the actual code to make whatever behavior we want here actually happen. |
I’d like to discuss how to proceed with this issue. @martinbonnin @BoD @BobaFetters @calvincestari Internally, we’ve had some past conversations about default arguments and how they are handled in apollo ios and kotlin.
In this issue, these concepts are combined. The client operation is defining a default argument for the What should we do in this case? I see a few possible solutions, but I’m not sure which (if any) of these are consistent with our current philosophy and design here.
Any preferences or opinions on how we should proceed here? |
My vote goes to not generating anything in codegen and just omit the variable in the request (Option B?): class MyQuery {
public init(arg: GraphQLNullable<MyType> = nil) {...}
} and send this over the wire: {
"query": "query myQuery($arg: MyType! = {})",
"variables": {}
} Then the server will know how to interpret the variable default value from the document and use the current Another fun edge case is when custom scalar are involved: input Filter {
before: Date
} query GetEvents($filter: {before: "2021-11-14"}) {
events(filter: $filter) {
id
title
# ...
}
} So now you need to call the user code to parse "2021-11-14" into some |
Yeah, that's definitely not what we are doing right now. And that does mean we end up parsing custom scalars when it's perhaps not necessary. But it also avoids this bug that you mentioned to me @martinbonnin. Making the change to this approach for all fields would probably be a pretty significant change. It would mean we would need to mitigate the aforementioned caching bug. It would also technically be a breaking change. Because currently, we initialize an actual input value struct with all the default values you provided in the operation definition (See the generated model example here). You can actually inspect and even mutate those values. So this approach would mean we would make the entire input struct Given the let query = PetSearchQuery()
query.filters.species = ["Bird"] // filters would be nil, and default value would only be interpreted at GraphQL execution time server-side.
client.fetch(query) { ... } But, I think this my proposed Option B is viable strictly for fields on an input type that are omitted from a default argument for that input type and have a default value on the schema. So given this input type: input MyType {
a: Float = 1.0
b: String
} And this definition: query myQuery($arg: MyType! = {b: "Hello World"}) Here the user is creating a default value for the We could generate the models: struct MyType {
public init(
a: GraphQLNullable<Double>,
b: GraphQLNullable<String>
) {
...
}
}
class MyQuery {
public init(arg: MyType = MyType(
a: GraphQLNullable<Double>,
b: GraphQLNullable<String> = "Hello World"
)
) {...}
}
// a is omitted from network request and uses schema's default value.
// b is sent as "Hello World"
let query = MyQuery(arg: MyType(a: nil))
// a is sent as 10.5
// b is sent as "Goodbye"
let query = MyQuery(arg: MyType(a: 10.5, b: "Goodbye")) Does that seem like a reasonable solution here? Is the fact that this only applies to fields on an input type that have a default value confusing? |
Could you force a specific value of let query = PetSearchQuery()
query.filters = PetSearchFilters(species = ["Bird"])
client.fetch(query) { ... }
class MyQuery {
public init(arg: MyType = MyType(
a: GraphQLNullable<Double>,
b: GraphQLNullable<String> = "Hello World"
)
) {...}
} I think that'd work. The 2 limitations I can foresee:
Regarding caching for Apollo Kotlin, I'm currently planning to either:
val argDefaultValue = mapOf(
"key" to "value",
"date" to "2023-11-15"
) Now for more fun times, there's the question of what to do with default values that are defined on the server and are subject to change without the cache knowing about it. Should we include those in the cache key or not? input Config {
language: String = "fr"
} query GetMessage($config: Config = {}) {
localizedMessage(config: $config)
} If we're using If we're using I'm not sure what to do there. Maybe Option A. and disabling this altogether makes more sense. But maybe only for cache users because there are legitimate usages for non-cache users? Not sure...
Inputs with default values are confusing because GraphQL doesn't have the notion of |
Summary
Example:
I have a type that has no required fields. There is a default argument for the only field
In my query, I provide a default argument
When I run code gen, there are no errors from the script and I get the following output:
However, because the initializer for
MyType
does not include either a default argument of1.0
ornil
, the generated code fails to compileMyQuery.graphql
=>Missing argument for parameter #1 in call
Version
1.5
Steps to reproduce the behavior
See the above code for an reproducible example
Logs
No response
Anything else?
No response
The text was updated successfully, but these errors were encountered: