Skip to content

Conversation

@bbakerman
Copy link
Member

@bbakerman bbakerman commented Sep 26, 2021

GraphQLDirective has been designed wrong. It's really the representation of the directive definition. Yet it and GraphqlArgument have been shoe horned into representing the instances of a directives applied to schema elements.

So this PR is s start to support for this. For backwards compat reasons the old information will be present and side by side GraphQLAppliedDirectives will be made available along with old wrong GraphQLDirectives

This is not done yet but I am putting up a PR for early feedback.

What needs to be done

✅ means its done
👍 means its QA checked
👎 means tis not right

  • Go through all the GraphqlDirectiveContainers and fix them up

    • Make sure they use the common builder ✅
    • Make sure they have the getAppliedDirectives methods ✅
    • Make sure the SchemaElement copying support code works - eg named children ✅
    • Make sure the common builder works via trick ✅
  • Make sure the schema validation catches non repeatable directive violations ✅ 👍

  • Go to schema gen and put in duplicate generation for applied directives along side the current directive code ✅

  • Put in a SchemaGen option that will NOT put in any old school objects - this will be opt in. ✅

  • Make sure the Schema Visitor code works and visits these elements ✅

  • Make sure the SchemaPrinter is working ✅

  • Make sure the Introspection gets the new applied directives ✅

  • Make sure graphql.execution.directives.QueryDirectives is working ✅

* However with directives on SDL elements, the value is specified in AST only and transferred into the GraphQLArgument object and the
* 'defaultValue' comes instead from the directive definition elsewhere in the SDL. You can think of them as 'instances' of arguments, their shape and their
* specific value on that directive.
*/
Copy link
Member Author

Choose a reason for hiding this comment

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

TODO - fix up JavaDoc - currently copied

*
* @return a value of type T which is the java value of the argument
*/
public static <T> T getArgumentValue(GraphQLInputType argumentType, GraphQLAppliedDirectiveArgument argument) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Help needed here @andimarek

* specific value on that directive.
*/
@PublicApi
public class GraphQLAppliedDirectiveArgument implements GraphQLNamedSchemaElement {
Copy link
Member

Choose a reason for hiding this comment

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

regarding naming: so the question is: has this actually anything todo with directives? How about GraphQLArgumentValue?

Copy link
Member Author

Choose a reason for hiding this comment

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

In some ways they dont have anything to do with directives - HOWEVER the only place where we have a "name" and value is in directives in SDL and "args" in queries

We never created a GraphQLArgumentValue in queries (just map<string,object> but yeah we could.

So I like GraphQLArgumentValue or maybe GraphqlAppliedArgument

Copy link
Member Author

Choose a reason for hiding this comment

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

renamed

return name;
}

@Override
Copy link
Member

Choose a reason for hiding this comment

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

does an applied directive actually have a description? Not 100% sure out of my head.

Copy link
Member Author

Choose a reason for hiding this comment

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

They do no have descriptions and the grammar does not allow it

However out interfaces call for it

public interface GraphQLNamedSchemaElement extends GraphQLSchemaElement {
    String getName();
    String getDescription();
    Node getDefinition();
}

Copy link
Member

Choose a reason for hiding this comment

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

probably should add Nullable then to it or add a comment

Copy link
Member Author

Choose a reason for hiding this comment

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

done

public String getDescription() {
return null;
}

Copy link
Member

Choose a reason for hiding this comment

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

This is @nullable

public static <T> T getArgumentValue(GraphQLInputType argumentType, GraphQLAppliedDirectiveArgument argument) {
return getInputValueImpl(argumentType, argument.getArgumentValue());
}

Copy link
Member

Choose a reason for hiding this comment

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

some question as above: do we actually have a Description?

Copy link
Member Author

Choose a reason for hiding this comment

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

See above - no

# Conflicts:
#	src/main/java/graphql/DirectivesUtil.java
#	src/main/java/graphql/schema/GraphQLArgument.java
#	src/main/java/graphql/schema/GraphQLEnumType.java
#	src/main/java/graphql/schema/GraphQLEnumValueDefinition.java
#	src/main/java/graphql/schema/GraphQLFieldDefinition.java
#	src/main/java/graphql/schema/GraphQLInputObjectField.java
#	src/main/java/graphql/schema/GraphQLInputObjectType.java
#	src/main/java/graphql/schema/GraphQLInterfaceType.java
#	src/main/java/graphql/schema/GraphQLObjectType.java
#	src/main/java/graphql/schema/GraphQLScalarType.java
#	src/main/java/graphql/schema/GraphQLUnionType.java
@bbakerman bbakerman changed the title MAJOR WIP - Applied Directives READY- Applied Directives Dec 13, 2021
* @return the directive to be included
*/
GraphQLDirective getDirective();
String getDirectiveName();
Copy link
Member Author

Choose a reason for hiding this comment

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

BREAKING CHANGE!!

We could put in a shimand defaulted method but do we want to? I dont think its use enough to warrant it

Thoughts?

directives.clear();
return this;
}

Copy link
Member Author

Choose a reason for hiding this comment

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

All these are moved to a common Builder that is type safe


@SuppressWarnings("unchecked")
@Internal
public abstract class GraphqlDirectivesContainerTypeBuilder<B extends GraphqlDirectivesContainerTypeBuilder<B, BASE>, BASE extends GraphqlTypeBuilder<BASE>> extends GraphqlTypeBuilder<BASE> {
Copy link
Member Author

Choose a reason for hiding this comment

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

Cool generics tricks to make sure they return the right type

@graphql-java graphql-java deleted a comment from andimarek Dec 13, 2021
import graphql.Internal;

@Internal
public final class Pair<T, U> {
Copy link
Member Author

Choose a reason for hiding this comment

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

A simple Pair class

Copy link
Member

Choose a reason for hiding this comment

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

think about guava code instead of home grown

* @return new instance of options
*/
public Options includeDirectives(Predicate<GraphQLDirective> includeDirective) {
return new Options(this.includeIntrospectionTypes, this.includeScalars, this.includeSchemaDefinition, this.includeDirectiveDefinitions, this.useAstDefinitions, this.descriptionsAsHashComments, includeDirective, this.includeSchemaElement, this.comparatorRegistry);
Copy link
Member Author

Choose a reason for hiding this comment

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

BREAKING CHANGE - again we could do work to make this NOT a breaking change BUT I dont think its worth it.

Thoughts?

directivesOriginalToNewNameMap.put(directiveName, newName);
return CONTINUE;
}

Copy link
Member Author

Choose a reason for hiding this comment

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

moved upwards and made common

@andimarek andimarek added this to the 18.0 milestone Feb 1, 2022
*
* @return a map of all the applied directives immediately on this merged field
*/
Map<String, List<GraphQLAppliedDirective>> getImmediateAppliedDirectivesByName();
Copy link
Member

@andimarek andimarek Feb 5, 2022

Choose a reason for hiding this comment

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

so here the order of the keys matter, right? We should communicate this clearly that the Map keys order reflects the order of the directives found.

Mh ... actually is that true with a merged field?
Maybe it is time to make this more explicit with a Field -> AppliedDirectives result value or so

Copy link
Member Author

Choose a reason for hiding this comment

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

Field -> AppliedDirectives is super weird in another way. That is we are kinda saying - we can't decide so.... here you go.

I mean we are more correct - only because we are no overly useful.

Copy link
Member Author

Choose a reason for hiding this comment

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

The code is

            final Map<Field, List<GraphQLDirective>> byField = new LinkedHashMap<>();
            final Map<Field, List<GraphQLAppliedDirective>> byFieldApplied = new LinkedHashMap<>();
            mergedField.getFields().forEach(field -> {
                List<Directive> directives = field.getDirectives();
                ImmutableList<GraphQLDirective> resolvedDirectives = ImmutableList.copyOf(
                        directivesResolver
                                .resolveDirectives(directives, schema, variables)
                                .values()
                );
                byField.put(field, resolvedDirectives);
                // at some point we will only use applied
                byFieldApplied.put(field, ImmutableKit.map(resolvedDirectives, GraphQLDirective::toAppliedDirective));
            });

So its in Merged field order - but that in itself might not be meaningful.

I guess this is NO worse than before in directive terms - so we can improve it later with a Field -> AppliedDirectives if we choose

return name;
}

@Override
Copy link
Member

Choose a reason for hiding this comment

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

probably should add Nullable then to it or add a comment

return null;
}

public List<GraphQLAppliedArgument> getArguments() {
Copy link
Member

Choose a reason for hiding this comment

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

calling it getAppliedArguments maybe?

import static graphql.execution.ValuesResolver.getInputValueImpl;

/**
* This defines an argument and its value that can be supplied to a graphql field (via {@link GraphQLFieldDefinition} during
Copy link
Member

Choose a reason for hiding this comment

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

I don't think this is a true this is used for field argument values. conceptually this might be true, but a GraphQLSchemaElement is not concerned with queries/

return null;
}

public Argument getDefinition() {
Copy link
Member

Choose a reason for hiding this comment

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

Can be null

*
* @return a map of all the applied directives immediately on this merged field
*/
Map<String, List<GraphQLAppliedDirective>> getImmediateAppliedDirectivesByName();
Copy link
Member

Choose a reason for hiding this comment

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

I think the bigger question is actually if applied directives should be used for queries AND schemas? conceptually they are the same, but code wise GraphQLApplieDirective is a schema element and not a query element

@bbakerman bbakerman merged commit a2d81a8 into master Feb 24, 2022
gnawf added a commit to atlassian-labs/nadel that referenced this pull request Jun 21, 2023
gnawf added a commit to atlassian-labs/nadel that referenced this pull request Jun 21, 2023
* Better union exclusion

* Fix tests

* Ignore applied directives

New behavior introduced in graphql-java/graphql-java#2562

* Fix counter

* Add tests
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.

3 participants