-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Parameterized introspection queries #2993
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
Parameterized introspection queries #2993
Conversation
| .type(typeRef("__Type"))) | ||
| .field(newFieldDefinition() | ||
| .name("specifiedByUrl") | ||
| .name("specifiedByURL") |
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 is wrong in both cases
directive @specifiedBy(url: String!) on SCALAR
It probably should be specifiedBy as a field name
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.
Even if we go with this new name - it a breaking change - anyone using it today would be broken
We need to add a new field and deprecate the old if we changed.
What does graphql-js do here?
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 is wrong in both cases
directive @specifiedBy(url: String!) on SCALARIt probably should be
specifiedByas a field name
unless I am mistaken, the field is the one here: https://spec.graphql.org/draft/#sec-Schema-Introspection.Schema-Introspection-Schema
Even if we go with this new name - it a breaking change - anyone using it today would be broken
We need to add a new field and deprecate the old if we changed.
What does graphql-js do here?
they currently only support the specifiedByURL field: https://github.com/graphql/graphql-js/blob/main/src/type/introspection.ts#L256 but expressed the same concern here: graphql/graphql-js#3156 . I see no issue with keeping specifiedByUrl and possibly deprecating it, similar to how you extended default introspection behavior with IntrospectionWithDirectivesSupport. But I see no similar approach in graphql-js.
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.
Let's put both fields in, deprecate the old and laugh about it on 20 years
bbakerman
left a comment
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 need a a new class here to do this Introspection building and it needs to use a Options pattern
| then: | ||
| result == [__type: [name: "MyScalar", specifiedByUrl: "myUrl"]] | ||
| result == [__type: [name: "MyScalar", specifiedByURL: "myUrl"]] |
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 am not sure how this relates to the change you have proposed
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 was just to keep the test passing. if you would like to keep support for the old field, it can be reverted.
| @PublicApi | ||
| public interface IntrospectionQuery { | ||
| public interface IntrospectionQuery { // todo iwds support | ||
| static <T> List<T> filter(T... args) { |
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.
private static - is that a thing in interfaces - we would not want this helper exposed
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 is not, but adding a builder class will fix this.
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.
graphql-js chose this naming
export interface IntrospectionOptions {
/**
* Whether to include descriptions in the introspection result.
* Default: true
*/
descriptions?: boolean;
/**
* Whether to include `specifiedByURL` in the introspection result.
* Default: false
*/
specifiedByUrl?: boolean;
/**
* Whether to include `isRepeatable` flag on directives.
* Default: false
*/
directiveIsRepeatable?: boolean;
export function getIntrospectionQuery(options?: IntrospectionOptions): string {
we could ape that
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've added an options builder with the fields on this interface: adafa6a#diff-77fac6499b3cfc7d87aebc6bc02e3ad3439fae0f5e549d5b5d39eaf09a052781R35
| boolean schemaDescription, | ||
| boolean inputValueDeprecation, | ||
| int typeRefFragmentDepth | ||
| ) { |
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.
A 6 parameter method is a terrible interface for generating introspection queries
getIntrospectionQuery(true, false, true, false, true, 7);
Looking at that code its near impossible to get signal on what you are getting - and Java doesnt have named parameters which makes it worse
I think this needs a builder object (aka options). See graphql.schema.idl.SchemaPrinter.Options for inspiration
I also think this should go into a class (not the interface)
Hence this might be
String INTROSPECTION_QUERY = IntrospectionQueryBuilder.build(IntrospectionQueryBuilder.defaultOptions())
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.
a great example of how IntelliJ has spoiled me, I can surely improve this with a builder.
| .type(typeRef("__Type"))) | ||
| .field(newFieldDefinition() | ||
| .name("specifiedByUrl") | ||
| .name("specifiedByURL") |
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.
Even if we go with this new name - it a breaking change - anyone using it today would be broken
We need to add a new field and deprecate the old if we changed.
What does graphql-js do here?
| .name("__TypeKind") | ||
| .description("An enum describing what kind of type a given __Type is") | ||
| .value("SCALAR", TypeKind.SCALAR, "Indicates this type is a scalar. 'specifiedByUrl' is a valid field") | ||
| .value("SCALAR", TypeKind.SCALAR, "Indicates this type is a scalar. 'specifiedByURL' is a valid field") |
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.
export interface IntrospectionScalarType {
readonly kind: 'SCALAR';
readonly name: string;
readonly description?: Maybe<string>;
readonly specifiedByURL?: Maybe<string>;
}
So it seems that graphql-js chose the name specifiedByURL and we chose specifiedByUrl
(Which is ironical because @andimarek drove this change in spec terms)
I think we should align to the reference implementation in this case - since it drives tooling behavior
But for backwards compat - we should "add" a new field and not change its name
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.
will adafa6a#diff-eba216571550152c40136483beff9a41311df7b7681264e65d8d0e7c866cff7eR403 suffice? I could revert or add new tests for it as well.
| .type(typeRef("__Type"))) | ||
| .field(newFieldDefinition() | ||
| .name("specifiedByUrl") | ||
| .name("specifiedByURL") |
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.
As above - add a new field and deprecate the old
| then: | ||
| parseExecutionResult(allTrueExecutionResult).every() | ||
| allTrueExecutionResult.data["__schema"]["types"].find{it["name"] == "Query"}["fields"].find{it["name"] == "tenDimensionalList"}["type"]["ofType"]["ofType"]["ofType"]["ofType"]["ofType"]["ofType"]["ofType"]["ofType"] == null // typeRefFragmentDepth is 7 | ||
| } |
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.
To be sure that IntrospectionQuery. INTROSPECTION_QUERY actualyl equals what it used to eqal can we hard code a test here
It would copy the OLD string value here and then check that the new generated constant is the same
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.
There are differences/improvements in the spacing and formatting of the string produced by the AST printer. Perhaps we can trim all whitespace and compare, or parse the old query with SchemaParser and reprint 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.
I've verified that the queries are equivalent via diff and added a test that verifies they are equal ignoring whitespace.
| then: | ||
| oldIntrospectionQuery.replaceAll("\\s+","").equals( | ||
| newIntrospectionQuery.replaceAll("\\s+","") | ||
| ) |
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.
Nice
* use ast builders and stub test * remove main and add test * restore jvm check * revert test spacing * fix test * revers build.gradle * use spec specifiedByURL capitalization * update another specifiedByURL test * refactor introspection query builder, restore and deprecate __Type.specifiedByUrl * test that AST printed introspection query is equivalent to original string
graphql-js has a number of parameters for generating introspection queries that include deprecated input fields, __Schema.description, __Directive.isRepeatable, __Type.specifiedByUrl, etc.. This branch adds support for these parameters, plus an arbitrary
TypeRefdepth, without changing the result ofIntrospectionQuery.INTROSPECTION_QUERY. Using the AST rather than string formatting to generate the introspection query makes the class about twice as verbose, but offers much more control.Two things to consider during review:
__Type.specifiedByUrlis resolved by__Type.specifiedByURLin my graphql-java projects, there were test cases and introspection datafetchers using specifiedByUrl, that I updated to match the current spec's capitalization.__Directive.args(includeDeprecated: Boolean)causes a validation error in those projects when I passincludeDeprecated: true, even though the argument shows up in introspection.