-
Notifications
You must be signed in to change notification settings - Fork 1.1k
This makes sure every introspection type actually has a concrete data fetcher #3004
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
Changes from all commits
50ea978
281fa4f
d20c258
8ec986f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -40,6 +40,7 @@ | |
| import java.util.Locale; | ||
| import java.util.Map; | ||
| import java.util.Set; | ||
| import java.util.function.Function; | ||
| import java.util.stream.Collectors; | ||
|
|
||
| import static graphql.Assert.assertTrue; | ||
|
|
@@ -65,6 +66,27 @@ private static void register(GraphQLFieldsContainer parentType, String fieldName | |
| introspectionDataFetchers.put(coordinates(parentType.getName(), fieldName), introspectionDataFetcher); | ||
| } | ||
|
|
||
| /** | ||
| * To help runtimes such as graalvm, we make sure we have an explicit data fetchers rather then use {@link graphql.schema.PropertyDataFetcher} | ||
| * and its reflective mechanisms. This is not reflective because we have the class | ||
| * | ||
| * @param parentType the containing parent type | ||
| * @param fieldName the field name | ||
| * @param targetClass the target class of the getter | ||
| * @param getter the function to call to get a value of T | ||
| * @param <T> for two | ||
| */ | ||
| private static <T> void register(GraphQLFieldsContainer parentType, String fieldName, Class<T> targetClass, Function<T, Object> getter) { | ||
| IntrospectionDataFetcher<?> dataFetcher = env -> { | ||
| Object source = env.getSource(); | ||
| if (targetClass.isInstance(source)) { | ||
| return getter.apply(targetClass.cast(source)); | ||
| } | ||
| return null; | ||
| }; | ||
| introspectionDataFetchers.put(coordinates(parentType.getName(), fieldName), dataFetcher); | ||
| } | ||
|
|
||
| @Internal | ||
| public static void addCodeForIntrospectionTypes(GraphQLCodeRegistry.Builder codeRegistry) { | ||
| // place the system __ fields into the mix. They have no parent types | ||
|
|
@@ -160,7 +182,7 @@ public enum TypeKind { | |
| .build(); | ||
|
|
||
| static { | ||
| register(__InputValue, "defaultValue", environment -> { | ||
| IntrospectionDataFetcher<?> defaultValueDataFetcher = environment -> { | ||
| Object type = environment.getSource(); | ||
| if (type instanceof GraphQLArgument) { | ||
| GraphQLArgument inputField = (GraphQLArgument) type; | ||
|
|
@@ -174,18 +196,41 @@ public enum TypeKind { | |
| : null; | ||
| } | ||
| return null; | ||
| }); | ||
| register(__InputValue, "isDeprecated", environment -> { | ||
| }; | ||
| IntrospectionDataFetcher<?> isDeprecatedDataFetcher = environment -> { | ||
| Object type = environment.getSource(); | ||
| if (type instanceof GraphQLArgument) { | ||
| return ((GraphQLArgument) type).isDeprecated(); | ||
| } else if (type instanceof GraphQLInputObjectField) { | ||
| return ((GraphQLInputObjectField) type).isDeprecated(); | ||
| } | ||
| return null; | ||
| }); | ||
| }; | ||
| IntrospectionDataFetcher<?> typeDataFetcher = environment -> { | ||
| Object type = environment.getSource(); | ||
| if (type instanceof GraphQLArgument) { | ||
| return ((GraphQLArgument) type).getType(); | ||
| } else if (type instanceof GraphQLInputObjectField) { | ||
| return ((GraphQLInputObjectField) type).getType(); | ||
| } | ||
| return null; | ||
| }; | ||
| IntrospectionDataFetcher<?> deprecationReasonDataFetcher = environment -> { | ||
| Object type = environment.getSource(); | ||
| if (type instanceof GraphQLArgument) { | ||
| return ((GraphQLArgument) type).getDeprecationReason(); | ||
| } else if (type instanceof GraphQLInputObjectField) { | ||
| return ((GraphQLInputObjectField) type).getDeprecationReason(); | ||
| } | ||
| return null; | ||
| }; | ||
|
|
||
| register(__InputValue, "name", nameDataFetcher); | ||
| register(__InputValue, "description", descriptionDataFetcher); | ||
| register(__InputValue, "type", typeDataFetcher); | ||
| register(__InputValue, "defaultValue", defaultValueDataFetcher); | ||
| register(__InputValue, "isDeprecated", isDeprecatedDataFetcher); | ||
| register(__InputValue, "deprecationReason", deprecationReasonDataFetcher); | ||
| } | ||
|
|
||
| private static String printDefaultValue(InputValueWithState inputValueWithState, GraphQLInputType type, GraphQLContext graphQLContext, Locale locale) { | ||
|
|
@@ -220,20 +265,20 @@ private static String printDefaultValue(InputValueWithState inputValueWithState, | |
| .build(); | ||
|
|
||
| static { | ||
| register(__Field, "args", environment -> { | ||
| IntrospectionDataFetcher<?> argsDataFetcher = environment -> { | ||
| Object type = environment.getSource(); | ||
| GraphQLFieldDefinition fieldDef = (GraphQLFieldDefinition) type; | ||
| Boolean includeDeprecated = environment.getArgument("includeDeprecated"); | ||
| return fieldDef.getArguments().stream() | ||
| .filter(arg -> includeDeprecated || !arg.isDeprecated()) | ||
| .collect(Collectors.toList()); | ||
| }); | ||
| register(__Field, "isDeprecated", environment -> { | ||
| Object type = environment.getSource(); | ||
| return ((GraphQLFieldDefinition) type).isDeprecated(); | ||
| }); | ||
| }; | ||
| register(__Field, "name", nameDataFetcher); | ||
| register(__Field, "description", descriptionDataFetcher); | ||
| register(__Field, "args", argsDataFetcher); | ||
| register(__Field, "type", GraphQLFieldDefinition.class, GraphQLFieldDefinition::getType); | ||
| register(__Field, "isDeprecated", GraphQLFieldDefinition.class, GraphQLFieldDefinition::isDeprecated); | ||
| register(__Field, "deprecationReason", GraphQLFieldDefinition.class, GraphQLFieldDefinition::getDeprecationReason); | ||
| } | ||
|
|
||
|
|
||
|
|
@@ -254,12 +299,10 @@ private static String printDefaultValue(InputValueWithState inputValueWithState, | |
| .build(); | ||
|
|
||
| static { | ||
| register(__EnumValue, "isDeprecated", environment -> { | ||
| GraphQLEnumValueDefinition enumValue = environment.getSource(); | ||
| return enumValue.isDeprecated(); | ||
| }); | ||
| register(__EnumValue, "name", nameDataFetcher); | ||
| register(__EnumValue, "description", descriptionDataFetcher); | ||
| register(__EnumValue, "isDeprecated", GraphQLEnumValueDefinition.class, GraphQLEnumValueDefinition::isDeprecated); | ||
| register(__EnumValue, "deprecationReason", GraphQLEnumValueDefinition.class, GraphQLEnumValueDefinition::getDeprecationReason); | ||
| } | ||
|
|
||
|
|
||
|
|
@@ -401,22 +444,22 @@ private static String printDefaultValue(InputValueWithState inputValueWithState, | |
| .name("specifiedByURL") | ||
| .type(GraphQLString)) | ||
| .field(newFieldDefinition() | ||
| .name("specifiedByUrl") | ||
| .type(GraphQLString) | ||
| .deprecate("see `specifiedByURL`") | ||
| .name("specifiedByUrl") | ||
| .type(GraphQLString) | ||
| .deprecate("This legacy name has been replaced by `specifiedByURL`") | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tweaked the comment while I was here - boy scout! |
||
| ) | ||
| .build(); | ||
|
|
||
| static { | ||
| register(__Type, "kind", kindDataFetcher); | ||
| register(__Type, "name", nameDataFetcher); | ||
| register(__Type, "description", descriptionDataFetcher); | ||
| register(__Type, "fields", fieldsFetcher); | ||
| register(__Type, "interfaces", interfacesFetcher); | ||
| register(__Type, "possibleTypes", possibleTypesFetcher); | ||
| register(__Type, "enumValues", enumValuesTypesFetcher); | ||
| register(__Type, "inputFields", inputFieldsFetcher); | ||
| register(__Type, "ofType", OfTypeFetcher); | ||
| register(__Type, "name", nameDataFetcher); | ||
| register(__Type, "description", descriptionDataFetcher); | ||
| register(__Type, "specifiedByURL", specifiedByUrlDataFetcher); | ||
| register(__Type, "specifiedByUrl", specifiedByUrlDataFetcher); // note that this field is deprecated | ||
| } | ||
|
|
@@ -497,38 +540,25 @@ public enum DirectiveLocation { | |
| .name("includeDeprecated") | ||
| .type(GraphQLBoolean) | ||
| .defaultValueProgrammatic(false))) | ||
| .field(newFieldDefinition() | ||
| .name("onOperation") | ||
| .type(GraphQLBoolean) | ||
| .deprecate("Use `locations`.")) | ||
| .field(newFieldDefinition() | ||
| .name("onFragment") | ||
| .type(GraphQLBoolean) | ||
| .deprecate("Use `locations`.")) | ||
| .field(newFieldDefinition() | ||
| .name("onField") | ||
| .type(GraphQLBoolean) | ||
| .deprecate("Use `locations`.")) | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I took the opportunity to remove this very old code |
||
| .build(); | ||
|
|
||
| static { | ||
| register(__Directive, "locations", environment -> { | ||
| IntrospectionDataFetcher<?> locationsDataFetcher = environment -> { | ||
| GraphQLDirective directive = environment.getSource(); | ||
| return new ArrayList<>(directive.validLocations()); | ||
| }); | ||
| register(__Directive, "args", environment -> { | ||
| }; | ||
| IntrospectionDataFetcher<?> argsDataFetcher = environment -> { | ||
| GraphQLDirective directive = environment.getSource(); | ||
| Boolean includeDeprecated = environment.getArgument("includeDeprecated"); | ||
| return directive.getArguments().stream() | ||
| .filter(arg -> includeDeprecated || !arg.isDeprecated()) | ||
| .collect(Collectors.toList()); | ||
| }); | ||
| }; | ||
| register(__Directive, "name", nameDataFetcher); | ||
| register(__Directive, "description", descriptionDataFetcher); | ||
| register(__Directive, "isRepeatable", environment -> { | ||
| GraphQLDirective directive = environment.getSource(); | ||
| return directive.isRepeatable(); | ||
| }); | ||
| register(__Directive, "isRepeatable", GraphQLDirective.class, GraphQLDirective::isRepeatable); | ||
| register(__Directive, "locations", locationsDataFetcher); | ||
| register(__Directive, "args", argsDataFetcher); | ||
| } | ||
|
|
||
| public static final GraphQLObjectType __Schema = newObject() | ||
|
|
@@ -562,24 +592,14 @@ public enum DirectiveLocation { | |
| .build(); | ||
|
|
||
| static { | ||
| register(__Schema, "description", environment -> environment.getGraphQLSchema().getDescription()); | ||
| register(__Schema, "types", environment -> { | ||
| GraphQLSchema schema = environment.getSource(); | ||
| return schema.getAllTypesAsList(); | ||
| }); | ||
| register(__Schema, "queryType", environment -> { | ||
| GraphQLSchema schema = environment.getSource(); | ||
| return schema.getQueryType(); | ||
| }); | ||
| register(__Schema, "mutationType", environment -> { | ||
| GraphQLSchema schema = environment.getSource(); | ||
| return schema.getMutationType(); | ||
| }); | ||
| register(__Schema, "directives", environment -> environment.getGraphQLSchema().getDirectives()); | ||
| register(__Schema, "subscriptionType", environment -> { | ||
| GraphQLSchema schema = environment.getSource(); | ||
| return schema.getSubscriptionType(); | ||
| }); | ||
| IntrospectionDataFetcher<?> descriptionsDataFetcher = environment -> environment.getGraphQLSchema().getDescription(); | ||
|
|
||
| register(__Schema, "description", descriptionsDataFetcher); | ||
| register(__Schema, "types", GraphQLSchema.class, GraphQLSchema::getAllTypesAsList); | ||
| register(__Schema, "queryType", GraphQLSchema.class, GraphQLSchema::getQueryType); | ||
| register(__Schema, "mutationType", GraphQLSchema.class, GraphQLSchema::getMutationType); | ||
| register(__Schema, "directives", GraphQLSchema.class, GraphQLSchema::getDirectives); | ||
| register(__Schema, "subscriptionType", GraphQLSchema.class, GraphQLSchema::getSubscriptionType); | ||
| } | ||
|
|
||
| public static final GraphQLFieldDefinition SchemaMetaFieldDef = buildSchemaField(__Schema); | ||
|
|
||
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 reorganised the registration to that the
registercall is done in simple lines and the fetchers are defined above.I also ordered them in the order of field definition from above.
So you can see that we defined name field first, so its DF registration is first and so on.