-
Notifications
You must be signed in to change notification settings - Fork 40
Add Input for supporting nulifying input object #30
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
Conversation
Could you please include a snippet of the generated code in your PR description? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@BenEmdon I just fixed the tests |
Might be worth adding some extra tests with the introduction of this new wrapper type. We did the same in https://github.com/Shopify/graphql_swift_gen/pull/15/files#diff-421decf1011f80ddb8a434c8e7d13e17R71. |
@BenEmdon Is this what you need? graphql_java_gen/support/src/test/java/com/shopify/graphql/support/IntegrationTest.java Line 170 in 8cd61aa
|
|
||
public final class Input<T> implements Serializable { | ||
|
||
public final T value; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I would prefer to use a getter here instead of a public field. Give us more flexibility in the future.
Also, should this be final? Do you have to construct a new input object if you want to change the wrapped value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it was an intention to make it immutable, following by best practises and similar to java Optional, guava Optional types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And as for the getter 👍
return ttl.value; | ||
} | ||
|
||
public Input<LocalDateTime> getTtlInput() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we unrwap these automatically to the underlying type automatically?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we have a separate getter, look line 1112
@@ -319,12 +323,12 @@ public class <%= schema_name %> { | |||
<%= generate_build_input_code(escape_reserved_word(field.camelize_name), field.type) %> | |||
<% end %> | |||
<% type.optional_input_fields.each do |field| %> | |||
if (this.<%= field.camelize_name %>Seen) { | |||
if (this.<%= escape_reserved_word(field.camelize_name) %>.defined || this.<%= escape_reserved_word(field.camelize_name) %>.value != null) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the second part of this conditional make sense? If defined
is false but there is a value I would argue that we shouldn't serialize it
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what do you mean? if defined is not false OR value not null then we serialize. There won't be case when we have defined == false and it has a value as there is no such constructor in Input
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point @wesleyjellis. The second part does not make sense anymore after we simplified the Input class. Just have quick discussion with @sav007 and we agreed 👍
@BenEmdon @dylanahsmith @see-mack @DanielJette any other comments / suggestions? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Small comment, but otherwise this looks good
* Created by henrytao on 9/7/17. | ||
*/ | ||
|
||
public final class Input<T> implements Serializable { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Personally, I don't like final classes because it restricts what future developers can do and it makes mocking impossible
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree that in some cases it makes more sense to make them open, but not here as:
- This is used internally in gen tool, what new features we are expecting to be added via inheritance and why?
- This class is implemented in a way that explicitly restricts the inheritance as we it always adds complexity of correctly supporting inheritance. Plus it's a value type (like sealed classes in Kotlin or enum in swift)
- Mocking what you want to mock, 2 values? Why not just use instance without mocking.
- From best practises by default classes should be closed (look for Kotlin, or Swift). And only make them open when you really need it and know what are you doing
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess it is mostly just a value class, I just don't see the value of restricting future developers from extending this. It's not like this is a super complicated class or is super critical.
I tend to default to trusting (future) developers to do the right thing.
Anyway, I am turning into a 🚲 🏠 discussion. This works fine
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks good 👍 Like the implementation. It's very similar to what's happinging over at Shopify/graphql_swift_gen.
Just one comment about tests that I feel is worth addressing. 👇
@@ -168,7 +169,7 @@ public void testOptionalFieldOnInput() throws Exception { | |||
@Test | |||
public void testUnsetOptionalFieldOnInput() throws Exception { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test should probably be renamed to testOptionalFieldOnInputAsUndefined
. It would also be worth having tests testOptionalFieldOnInputAsExplicitNull
and testOptionalFieldOnInputAsInputValue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Okie. I will add them.
Doesn't #29 already provide support for nullifying an input object? Is this just about changing the way that is done to make it consistent with graphql_swift_gen? |
@Test | ||
public void testOptionalFieldOnInputAsExplicitNull() throws Exception { | ||
String queryString = Generated.mutation(mutation -> mutation | ||
.setInteger(new Generated.SetIntegerInput("answer", 42).setTtlInput(Input.<LocalDateTime>value(null))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why do this instead of what is done in testOptionalFieldOnInput
which uses .setTtl(null))
instead of .setTtlInput(Input.<LocalDateTime>value(null)))
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It tests different way to set null
as explicit input value to be sent to the server
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What I don't understand is why we are adding another way to do this when we already have a way.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean why do we have another setter like setTtlInput(Input<LocalDateTime>)
?
To be able to do this: setTtlInput(Input.undefined())
that means ttl
is undefined don't send it to the server at all (don't send null
).
Why do we have 2 of them? Because of MBSDK depends on this and if we remove existing one it means breaking changes. So we keep 2 of them for now.
@dylanahsmith we decided to go with this approach for different reasons, and to be aligned with swift one of them. |
What reasons? None have been given in this PR other than alignment with swift
Specifying input arguments is already different in java since the builder pattern was needed to specify arguments due to the lack of support for keyword arguments in java. However, java doesn't have the problem that swift had where we couldn't distinguish between a It feels like we are working around a problem that the java implementation doesn't have. |
Sorry if we didn't let you know but we had a lot of discussion in Slack and in this issue: https://github.com/Shopify/android/pull/721 And decision been made to go with this PR. |
Sorry I'm not following you. The problem is that we have to differentiate 2 meanings of
|
The query builder pattern represents all three of those states. Using ttl as an example:
so again, this PR doesn't add the ability to support sending Perhaps that approach was confusing. Also, it looks like handling input that may or may not be specified would be more annoying since it would require a conditional to either avoid the call to |
the API was really confusing, like set to null what it really means? Will it be sent or not. And by default all values are null but they won't be sent to server. Then how to reset field to null that is not going to be sent. With Input value type the API more explicit. |
Yes, I would say the GraphQL API is confusing to distinguish between these. It isn't clear what it means. However, it does make sense that
Oh right, those getters would be very confusing and didn't provide a way to find out if a field was unset or set to
|
#This PR supports nulifying for optional input type fields. It also removes
seen
flag.Sample generated code