Hi, the printSchema function in "graphql/utilities/schemaPrinter" seems to forget the directives on field definitions.
import {buildSchema} from "graphql/utilities/buildASTSchema"
import {printSchema} from "graphql/utilities/schemaPrinter"
const schema = buildSchema(`
type Person {
id: ID!
name: String @skip(if: true)
}
type Query {
people: [Person]
}
`)
console.info(printSchema(schema))
current output:
type Person {
id: ID!
name: String
}
type Query {
people: [Person]
}
expected output:
type Person {
id: ID!
name: String @skip(if: true)
}
type Query {
people: [Person]
}
Tested against graphql version: 0.9.6
I already digged a bit deeper into schemaPrinter and it seems that there is some special handling for directives going on:
1.) printSchema filters directives down to "skip", "include" and "deprecated"
2.) @ deperected seems to be handled
This is maybe better addressed in a separate issue, but I would like to add my own custom directives and get them also properly handled by printSchema.
Would you consider a PR for proper handling of spec directives as well as custom ones?
This is correct behavior. Directives are artifacts of the GraphQL language, and not properties of a GraphQL schema. While directives are allowed to be used in the GraphQL schema language, they exist as language-level metadata which different tools may interpret in different ways.
For example, as you pointed out @deprecated is a directive which the schema generation can use to fill in the actual field deprecation information in the schema. Tools like graph.cool or other code generation service often use these directives to describe what kind of resolvers or data storage to use when treating this document as a source of truth.
However the opposite is not the case. Since a schema is an in-memory data structure artifact, and not simply a representation of the GraphQL language, it does not have a concept of directives on its own. Though utilities like printSchema(), which produce GraphQL language from a GraphQL schema, may use directives to express other concepts, the one that it currently uses is @deprecated.
However to your example, @skip applied to a type definition has no semantic meaning in GraphQL (in fact, if you ran validate() on this document, it would likely point this out), and thus is not used when producing the GraphQL schema. When you then use printSchema() to produce the text again, since the directive had no semantic meaning and wasn't used, it doesn't appear in the printed output.
IMO lacking of mechanism to transmit any metadata in schema (even descriptions are stripped off) is a serious flaw and actually its hard for me to understand described harm.
Directives are the closest apple, but the real need is to have payload.
@mrjj Agree, but this issue is beyond the scope of graphql-js and should be handled in GraphQL spec. Here is the issue about adding it to the spec: https://github.com/facebook/graphql/issues/300
@IvanGoncharov the issue that during 2 last years of development resources are investing in parser, but not in language and seeing parser and schema description language as a different things with different policy is completely shizophenic.
@jzimmek
This is correct behavior. Directives are artifacts of the GraphQL language, and not properties of a GraphQL schema.
I hope you will never meet this situation in third-party project:
- we need anonymous lambda in our language
- anonymous lambdas are highly supported
- but they are not working, compiler just ignoring them
- right, because they are supported by language syntax and reference parser standards but completely contradicts our language standards, so we've invested resources to hardcode restrictions on language constructions that could be potentially used to imitate anonymous lambdas.
- but we really need anonymous lamdas....
Every day students write a ten new parsers even with formal grammar description support and other bells and whistles. They have no value.
Value is in maintaining industry standards that are comfortable to use, have working reference implementation, extendable and through this making integration and development easy. I don't expect that e.g. Brotobufs or SOAP or RDP or whatever extensions being defined in standards will be intentionally restricted in reference implementation to make it non-extandable by no reason.
Standards covering e.g. JSONSchema or Thrift are not separated on syntax and language, they are just JSONSchema or Thrift standards and every implementation just implementing this standard from 1% to 200% and reference implementation is you know reference for this.
If you want to reproduce the definition along with the directives you can just print all the astNodes.
import { print, isSpecifiedScalarType, isSpecifiedDirective } from 'graphql';
export function printSchemaWithDirectives(schema) {
const str = Object
.keys(schema.getTypeMap())
.filter(k => !k.match(/^__/))
.reduce((accum, name) => {
const type = schema.getType(name);
return !isSpecifiedScalarType(type)
? accum += `${print(type.astNode)}\n`
: accum;
}, '');
return schema
.getDirectives()
.reduce((accum, d) => {
return !isSpecifiedDirective(d)
? accum += `${print(d.astNode)}\n`
: accum;
}, str + `${print(schema.astNode)}\n`);
}
@bhoriuchi Great snippets, do you think it's possible to also convert comment descriptions?
I was using printSchema(buildSchema(schemas), { commentDescription: true }) to convert our Apollo compatible schemas into the pathetic AppSync compatible version. It went well until we tried their subscriptions via @aws_subscribe(mutation: [...]).
AWS AppSync has their own custom directives, but their GraphQL engine is also ancient. It does not support type extension, uses comment descriptions, hiccups left and right...
@vicary I'm not sure as I have no experience with AppSync. The code I provided relies on the native graphql-js print function but you can get the description from the astNode and add it in if its not being printed. https://github.com/graphql/graphql-js/blob/master/src/language/ast.js#L540
@bhoriuchi Thanks! I wanted description of type defs instead of directive defs, but I get the idea.
I also noticed that all of my extend type ... is missing, is it possible to combine all type extensions as if they are one type def?
Ended up digging too deep and creating appsync-schema-converter myself.
Most helpful comment
If you want to reproduce the definition along with the directives you can just print all the astNodes.