-
Notifications
You must be signed in to change notification settings - Fork 275
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
feat: Add Schema Directives to SDL #952
Conversation
Dont close #148, still needs a plugin for it to make is nice for people to use (should be trivial once this is merged) |
62577bb
to
8524a96
Compare
directiveUse: NexusDirectiveUse, | ||
directiveDef: GraphQLDirective | ||
): ReadonlyArray<ArgumentNode> { | ||
return mapObj(directiveUse.args ?? {}, (val, key) => { |
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 believe args
in NexusDirectiveUse
is an array since it uses MaybeArgsFor
which maps to an array, I might be missing something though.
It is typed as any
in my editor currently.
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.
Hmm... Need to check on that
* @example | ||
* directives: [useDirective('ExampleDirective', { arg: true })] | ||
*/ | ||
directives?: Directives |
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.
General comment: Do you think it would be possible to type the Directives
here to make sure the location matches the type? Currently it would fail at build time so I guess it's not too bad.
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.
Yeah I had initially tried this api as:
directives(t) {
t.MyDirective(args)
t.OtherDirective(args)
}
Where the members were dynamically based on directive declarations & where they can be positioned. It generally worked, but the types were pretty gnarly & seemed to be a lot of complexity for something that could just be a simple runtime error.
export function directive<DirectiveName extends string>( | ||
config: NexusDirectiveConfig<DirectiveName> | ||
): NexusDirectiveDef<DirectiveName> { | ||
assertValidName(config.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.
We also need to assert the name of the arguments too, is that done somewhere else?
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.
Actually recently found that we don't do this for field names, but we should 😬
* locations: ['FIELD_DEFINITION'], | ||
* }) | ||
*/ | ||
args?: ArgsRecord |
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.
General question since I am not sure, are we dealing with loops (direct or indirect) inside the directive?
https://spec.graphql.org/June2018/#sec-Type-System.Directives (see the validation at the end)
Say:
export const TestDirective = directive({
name: 'TestDirective',
args: {
someArg: arg({ type: 'TestType'})
},
locations: ['FIELD_DEFINITION'],
})
export const TestType = objectType({
name: 'TestType',
definition(t) {
t.string('myField', { directives: [TestDirective({ myField: "bob" })]})
}
})
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.
No, good catch - need to do that
|
||
export function printSchemaWithDirectives(schema: GraphQLSchema): string { | ||
return printFilteredSchemaWithDirectives(schema, (n) => !isSpecifiedDirective(n), isDefinedType) | ||
} |
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 don't think we need a custom printer, all graphql versions that nexus support have a schema printer that does print the directives:
https://github.com/graphql/graphql-js/blob/v14.5.8/src/utilities/schemaPrinter.js
https://github.com/graphql/graphql-js/blob/v15.5.1/src/utilities/printSchema.js
And they should be exported by graphql/utilities
.
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 believe it only prints the directives declarations, not the directive uses on the SDL types/fields
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.
Good point, bit annoying that we have to copy paste/maintain all that code. We should reference the commit it was taken from and maybe the diff vs the original to reduce upgrade burden.
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.
Yeah, I'm not too worried about it in the sense that since it's just printing code the paths are pretty simple and easy to validate correctness of (not a lot of business logic that you'd otherwise be worried about with copy/paste).
) | ||
assertNoMissingTypes(schema, missingTypes) | ||
runAbstractTypeRuntimeChecks(schema, finalConfig.features) | ||
return { schema, schemaTypes, tsTypes } | ||
} | ||
|
||
/** Builds the schema, we may return more than just the schema from this one day. */ | ||
export function makeSchemaInternal(config: SchemaConfig) { |
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.
Do we want to export that anymore?
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.
Yeah I think I'm using it in the tests, I don't believe it's publicly exported on the module
nexus: schemaExtension, | ||
}, | ||
directives: [...specifiedDirectives, ...Object.values(customDirectives)], | ||
...schemaDirectives, |
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 have a TS error in editor for that line Property 'operationTypes' is missing in type 'ScalarTypeDefinitionNode' but required in type 'SchemaDefinitionNode'
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 feel this is misleading for future changes, this returns an AST node. If some would need to override another property in the astNode
they might override the astNode
set by schemaDirectives
.
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.
Yeah agreed, meant to come back to this / mark this as a TODO
@@ -869,6 +931,7 @@ export class SchemaBuilder { | |||
...config.extensions, | |||
nexus: new NexusInputObjectTypeExtension(config), | |||
}, | |||
...this.maybeAddDirectiveUses('INPUT_OBJECT', config.directives), |
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 said it in the makeSchema
, but I want to drive the point, since this adds a whole astNode
I feel that someone that might want to modify the AST node for another purpose will get blocked by this code or might override it entirely. I think we should at the very least wrap that in an array and a merge function or even better if only the directives
field of the node was returned by maybeAddDirectiveUses
(free and member functions) and the astNode
was generated by another method.
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 feel that someone that might want to modify the AST node for another purpose will get blocked by this code or might override it entirely
Yeah, fair points - I had considered that and was thinking of just crossing that bridge if/when it came to it, since astNode
isn't part of the public api for any of the nexus builder fn's, I wasn't sure if that would actually happen in practice.
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.
In theory no, we should never touch the AST node since it is supposed to the result of the parsing of the schema.
I still think it should be explicit in the code that we are converting the directives to an AST node. Something like:
this.createAstNode([this.maybeAddDirectiveUses])
I suspect that a future release of graphql will add backend directives in the spec, so it will just be easier to move to that then.
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.
@tgriesser what would be the shortest path to unblock this pull request so it can go through?
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.
@zingerj this is the blocker.
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.
Going to punt on this for now - going to assume that if you need to modify the ASTNode you can use something else more specialized like graphql-tools
Is there an estimated time for this new feature? 👀 |
* main: feat: ability to rename root types (#976) docs: Remove `.ts` extension from import statement (#974) fix: revert declareInputs default to false (#975) fix: printer imports, follow up from #967 (#970) fix: plugin inputFieldDefTypes (#919) fix: add globalHeaders to imports for global def file (#969) fix: correct imports for changes in #967 (#968) feat: ConfiguredTypegen for splitting global types & configuring input type emission (#967) Revert "fix: #921 wrong typegen for nullable values w/ default (#965)" (#966) fix: #921 wrong typegen for nullable values w/ default (#965)
Going to probably work on it a bit today/tomorrow. |
Does it work on export const InputType = inputObjectType({
name: 'InputType',
directives: // here!
definition(t) {
t.nonNull.string('key', {
directives: // here!
})
t.int('answer')
},
}) |
Any progress on this one? |
What's a blocker of merging this PR? Failed checks? |
@davidsoderberg & @yasaichi just to let you know... I needed this for Apollo Federation support and ended up switching to type-graphql a month ago. Maybe it can help you too |
I also ended up switching to type-graphql... :( |
Hey all, what's the latest on getting this merged? Looks like there's one failing test; is that it to get this across the line? Thanks for your work here! |
When can we expect this to get merged? Federation relies on directives. |
This is great feature - thank you!! |
[FYI] To those who can't wait for this PR merged
|
Please merge this! |
* main: (33 commits) fix: incorrect logic in backward pagination (#1084) fix: update snapshots for change in #1083 chore: change facebook.github.io to relay.dev links (#1083) feat: allow specifying custom directives in makeSchema (#1065) Update comment for `shouldGenerateArtifacts` (#1057) feat: Run both formatTypegen and prettier formatter if given (#1042) feat: allow specifying custom directives in makeSchema (#1064) feat: Use ReadonlyArray in typings (#1041) chore: use dripip reusable workflow docs: Update npm badge v1.3.0 chore: add test confirming v16 schema compat (#1054) chore: update github workflows (#1053) feat: add GraphQL 16 support (#977) chore(docs): fix typos (#1005) chore(docs): fix typo 06-chapter-5-persisting-data-via-prisma.mdx (#1007) chore(docs): fix typo 04-why-nexus.mdx (#1008) chore(docs): fix typo 030-neuxs-framework-prisma-users.mdx (#1016) chore(docs): fix typo 05-chapter-4-testing-your-api.mdx (#1023) chore(docs): fix typo in 07-chapter-6-testing-with-prisma.mdx (#1024) ...
* main: chore: attempt to fix the dripip workflow, again chore: update the dripip workflow, bump lint-staged
This is merged, and is now released under No official plugin for federation, but the primitives are there - feel free to open a PR if you make progress on something generic and would like to add a plugin to the core. Additionally, please give any feedback or let me know if you run into issues with it, will get this into an official |
@tgriesser - Thank you! any date we expecting this to be released? |
@tgriesser - Please release this soon with the docs. |
Closes: #947, #53
Helps with: #148
WIP: Still needs docs / additional tests / cleanup of a bit of the builder layer / fixing types
Adds a
directives
property to all definition props which takes an array of directives. Adds them to anastNode
which are then printed via a custom schema printer.Definition:
Use:
or
Prints:
See changes in the
kitchenSink/__app.ts
for more example uses.