Skip to content

Conversation

@kaqqao
Copy link
Contributor

@kaqqao kaqqao commented Sep 10, 2018

This both fixes #1208 and adds traversal support for directives as a general feature.

Still needs tests, which I'll add tomorrow.

@kaqqao kaqqao changed the title Add supporting for traversing directives WIP - Add supporting for traversing directives Sep 11, 2018
@bbakerman bbakerman added this to the 11.0 milestone Sep 13, 2018
@bbakerman bbakerman requested a review from andimarek September 13, 2018 21:26
@bbakerman
Copy link
Member

Thanks for this. This will be a powerful capability over time. Out directive journey is really just beginning and hence we need to make them more first class.

This is great PR. Once you have the test I want to merge it.

👍 💯

@andimarek
Copy link
Member

@exbe can you have a look at this? your thoughts? thx

@kaqqao kaqqao changed the title WIP - Add supporting for traversing directives Add supporting for traversing directives Sep 15, 2018
Map<String, GraphQLType> allTypes(final GraphQLSchema schema, final Set<GraphQLType> additionalTypes) {
List<GraphQLType> roots = new ArrayList<GraphQLType>() {{
add(schema.getQueryType());
List<GraphQLType> roots = new ArrayList<>();
Copy link
Contributor Author

Choose a reason for hiding this comment

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

The anon class keeps a reference to the outer instance, which is irrelevant here but I figured it might become relevant as the code gets refactored in the future. This is the only reason I removed the anon class.

@kaqqao
Copy link
Contributor Author

kaqqao commented Sep 15, 2018

Added the additional directives as roots for type reference resolution traversal which I was missing the first time and the tests for #1208 and general directive reachability during the traversal.

I think it's now ready, but I'm happy to add/change anything if needed.

final Map<String, GraphQLType> typeMap = schema.getTypeMap();
TRAVERSER.depthFirst(new GraphQLTypeResolvingVisitor(typeMap), typeMap.values());
List<GraphQLType> roots = new ArrayList<>(typeMap.values());
roots.addAll(schema.getDirectives());
Copy link
Contributor Author

@kaqqao kaqqao Sep 15, 2018

Choose a reason for hiding this comment

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

GraphQLArgument had previously only been found on a GraphQLFieldDefinition which itself could only been found on a GraphQLObjectType which is regular type. As a result, a type reference in an argument would only be possible within a hierarchy of another type. Now, a GraphQLArgument can be found directly on a directive which is not a real type. As a result, a type reference on an argument can be found outside of a hierarchy of a real type, thus it will not be in the schema's type map. And for those to be replaced correctly, directives need to be added as roots. This may result in a duplicated traversal in some cases (if the type of the directive argument is complex and has a reference inside, and does end up in the type map), but outside of a small performance hit, it has no negative consequences.

I guess it would also be possible for GraphQLTypeCollectingVisitor to also keep track of all top-level references in a separate collection. I'm personally OK with my current approach, but am open to changing it if you wish.

Copy link
Contributor

Choose a reason for hiding this comment

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

I see what you are proposing, but not sure I have good enough picture about possible use and edge cases.

Do we allow directives with same names and different arguments?
Can I have a directive, which accepts a complex type which has yet another directive and another type?
Will it matter to distinguish type traversal from traversal of a type under directive(s)?

Please bear with my questions, I will try to catch up with directives and clear my vision.
So far most simplest solution is probably the best strategy :)

Copy link
Contributor Author

@kaqqao kaqqao Sep 18, 2018

Choose a reason for hiding this comment

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

  • Directives with same names but different arguments - As far as I know, this is not allowed (same unique-name policy as with types). There might already be such a validation in place.
  • Directives accepting complex types with further types and directives - Yes, I believe this is legit and is covered.
  • Type traversal vs directive type traversal - I don't think there should be anything special about types under directives.

The edge cases I was referring to are mostly about type reference replacement. E.g. if a directive refers to a type found elsewhere in the schema, the reference must be replaced correctly. And vice versa, if another schema element referes to a type first defined on a directive it should work as usual. I tried to cover this in the tests.

For what it's worth, I've been using my fork for a few days now in rather complex scenarios (directives with complex types with references to other complex types with further directives) and it's behaving exactly as I'd expect.

Copy link
Contributor Author

@kaqqao kaqqao Sep 18, 2018

Choose a reason for hiding this comment

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

But what I think is worth pointing out is that I don't change any rules in this PR. Everything doable with my changes is also doable without them. You can already add directives to anything, assign arguments of complex types, add further directives to those arguments etc.

All that the change does is make directives reachable by the traverser and, using that, make sure the reference replacement and type collection work in a consistent manner across schema elements.

@exbe
Copy link
Contributor

exbe commented Sep 18, 2018

I have to play with it, but so far looks good... let me try to run it.
Great job @kaqqao!

@kaqqao kaqqao changed the title Add supporting for traversing directives Add support for traversing directives Sep 18, 2018
Copy link
Member

@andimarek andimarek left a comment

Choose a reason for hiding this comment

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

LGTM @kaqqao .. great work.

As mentioned here I think we have a general issue with being GraphqlType the Node root interface, but this PR is consistent with the current approach so I am fine with treating GraphQLDirective as GraphQLType.

Is this PR ready for merge in your opinion? You mentioned before tests are missing. I see good tests, but maybe you have more in mind.

Thanks!

@kaqqao
Copy link
Contributor Author

kaqqao commented Oct 1, 2018

Yes, I think it's ready. I've added the tests, indeed.

@andimarek andimarek merged commit af07d80 into graphql-java:master Oct 2, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Type collection and reference replacement does not descend into directive arguments

4 participants