-
Notifications
You must be signed in to change notification settings - Fork 96
Improving GraphQL type handling and upgrading testng #288
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
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.
Hi, thanks for the PR !
We're now helping on project maintenance, I have reviewed and tested your code. I was quite confused trying to understand what you were trying to achieve, the test case does not reflect any new possibility you introduced.
In the end, I think the support for Optional can be achieved by just adding a new case in buildArg.
Also we did not understand what was the GraphqlUndefined - from what I've seen the class itself is actually never really used .. ?
See comments inline
Optional<Object> optionalArg = Optional.ofNullable(arg); | ||
if (!optionalArg.isPresent()) { | ||
return 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.
This creation of optional is useless and makes the code much more confusing and complex to understand. You just test that optional isPresent, otherwise return null. An optional which always have a value is not an optional anymore.
if (arg == null) { | ||
return null; | ||
} else { | ||
return Optional.ofNullable(buildArg(subType, new GraphQLUndefined(), arg)); |
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.
Here I think we have a major issue : the second parameter should be graphqlType, we are just creating an optional of the arg parameter. If subtype is another parameterized type (a list), it won't be correctly parsed. Anyway it's strange to use GraphqlUndefined here since the value is defined.
|
||
import java.util.List; | ||
|
||
public class GraphQLUndefined implements GraphQLType { |
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 don't understand the meaning or usage of this class. It's only used in buildArg, in a value that will be thrown away just after.
public void queryWithUndefinableParameters() { | ||
GraphQLSchema schema = newAnnotationsSchema().query(QueryUndefinedParameter.class).build(); | ||
|
||
GraphQL graphQL = GraphQL.newGraphQL(schema).build(); | ||
ExecutionResult result = graphQL.execute("{ something(code: {firstField:\"a\",secondField:\"b\"}) otherthing(code: {secondField:\"c\"}) listthings somethingWithOneField(code: {}) }", new QueryUndefinedParameter()); | ||
assertTrue(result.getErrors().isEmpty()); | ||
assertEquals(((Map<String, String>) result.getData()).get("something"), "ab"); | ||
assertEquals(((Map<String, String>) result.getData()).get("otherthing"), "c"); | ||
assertEquals(((Map<String, String>) result.getData()).get("listthings"), "was null"); | ||
assertEquals(((Map<String, String>) result.getData()).get("somethingWithOneField"), "was undefined"); | ||
} | ||
|
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.
Since the goal of this story (from my understanding) is to make the distinction between {} and {value: null} , we should at least have a test that shows it. The use cases described here already work without the proposed changes.
} else { | ||
return arg; | ||
return optionalArg.orElse(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.
again, return arg
was much more clear
if (parameters[0].getType().isAssignableFrom(Optional.class)) { | ||
return constructNewInstance(constructor, arg); | ||
} else { | ||
return constructNewInstance(constructor, optionalArg.orElse(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.
I don't see how this case can work. The constructor has one single parameter which is assignable from arg.getClass()
(Map, for example) and at the same time Optional.class , and in that case , you don't pass the optional but the arg ? otherwise, you keep the previous behaviour , equivalent to return constructNewInstance(constructor, arg);
? In that case, rather keep the previous code
} else { | ||
List<Object> objects = new ArrayList<>(); | ||
Map map = (Map) arg; | ||
Map map = (Map) optionalArg.orElseGet(Collections::emptyMap); |
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.
OptionalArg isPresent (you test that at the beginning). Keep Map map = (Map) arg;
if (((ParameterizedType) p).getRawType() == Optional.class) { | ||
if (!optionalArg.isPresent()) { | ||
return null; | ||
} else { | ||
Type subType = ((ParameterizedType) p).getActualTypeArguments()[0]; | ||
return Optional.ofNullable(buildArg(subType, graphQLType, arg)); | ||
} | ||
} else { | ||
List<Object> list = new ArrayList<>(); | ||
Type subType = ((ParameterizedType) p).getActualTypeArguments()[0]; | ||
GraphQLType wrappedType = ((GraphQLList) graphQLType).getWrappedType(); | ||
|
||
for (Object item : ((List) arg)) { | ||
list.add(buildArg(subType, wrappedType, item)); | ||
for (Object item : ((List) optionalArg.orElseGet(Collections::emptyList))) { | ||
list.add(buildArg(subType, wrappedType, item)); | ||
} | ||
return list; | ||
} |
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.
Keep this whole case unchanged, and move the next case (p instanceof ParameterizedType && ((ParameterizedType) p).getRawType() == Optional.class)
before the list one
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.
Hi, thanks for the PR !
We're now helping on project maintenance, I have reviewed and tested your code. I was quite confused trying to understand what you were trying to achieve, the test case does not reflect any new possibility you introduced.
In the end, I think the support for Optional can be achieved by just adding a new case in buildArg.
Also we did not understand what was the GraphqlUndefined - from what I've seen the class itself is actually never really used .. ?
See comments inline
We will be closing the PR for the time being, there are interesting improvements in your initial proposal but it required some work to be integrated into the codebase. We don't have the bandwidth to do these changes ourselves for now, but if you're still interested, feel free to re-open the PR and submit the requested changes. Thanks, |
Partial update to entities
The purpose of this PR is to enable updation of selective entity fields. By introducing
Optional
, it also differentiatesnull
fromundefined
Let's assume an entity with 5 fields and the requirement is to update only one field to
null
. We can set the value of that field tonull
and omit other fields in a mutation query.Changelog
javax.xml.bind.DatatypeConverter
6.9.10
to7.4.0
GraphQLUndefined
typeMethodDataFetcher
:Nullable
handling usingOptional
Optional
field typesRawType
in additional toWrpperType
GraphQLInputTest
updated to testOptional
field types in GraphQL queries