-
Notifications
You must be signed in to change notification settings - Fork 242
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(federation): Implement @core
and @join
directives to replace CSDL
#588
Conversation
67f7f1f
to
f1e0a8a
Compare
I went with We need the |
@@ -661,6 +662,7 @@ export function composeServices(services: ServiceDefinition[]): CompositionResul | |||
return { | |||
schema, | |||
composedSdl: printComposedSdl(schema, services), | |||
coreSchema: printCoreSchema(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.
Do we need to continue to support both the old CSDL and core schema? If so, should we avoid printing both and expose those as separate print functions that act on the 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 consider composedSdl
to be something that we ultimately remove, but I think you make a fair point that it's excessive to do both prints. Seems like we have a few reasonable options:
- leave as is and just remove it soon (and mark
composedSdl
as@deprecated
?) - remove
composedSdl
in this PR as a breaking change composeAndValidate
is just responsible for returning theschema
, most consumers will then pass that to theprintXyz
fn of their choice - also breaking. Also, undeprecateschema
. Not the most ergonomic end result, but maybe the most future proof?- ?
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.
Is anyone using composedSdl
, or was that added just to support CSDL? What would break if we removed that today?
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.
Just returning a schema would only work if we were sure a GraphQLSchema
could always fully represent a core schema. I'd love for that to be true, but currently there is no established mechanism for core schema metadata, and we just rely on our custom extensions
for composition results. (I can see a world where we define a CoreSchema
subclass that has further guarantees about how core schema metadata is represented under extensions
, but that's not something we can do in the short term.)
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.
Also, should coreSchema
be coreSchemaSDL
or coreSDL
maybe?
@@ -211,15 +228,15 @@ function printFederationTypeDirectives(type: GraphQLObjectType): string { | |||
const restEntries = Object.entries(restKeys); | |||
|
|||
return ( | |||
`\n @owner(graph: "${ownerService}")` + | |||
`\n @join__owner(graph: ${ownerService.toUpperCase()})` + |
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 think we may want a separate Graph
type (in TypeScript) that would encapsulate metadata about a graph, including the graph name and endpoints. That means we could store the graph name uppercased in there and don't have to remember to do that when printing.
if (serviceName) { | ||
printed += serviceName.toUpperCase(); | ||
} |
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.
The generated core schema was invalid because the `@join__type` directive definition should be repeatable and have `key` as its argument (instead of `requires` and `provides`). We would also generate a `@join__field` directive without a graph name in case `serviceName` was undefined, which would lead to a syntax error when parsing. This commit also renames `@http` to `@join__endpoint` and includes a directive definition for it (which was missing before).
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.
@glasser I went ahead and cherry-picked @martijnwalraven's changes (using @join__endpoint
). Let me know if you have any concerns with that, but it seemed reasonable to me.
@martijnwalraven Does it have to be uppercased? Does that mean that we are getting the subgraph ID from somewhere (the subgraph name in managed federation, the service list, etc) and then transforming it to uppercase just for the sake of the enum? Could we just... not do that? Or I guess do we have to do some transform anyway because the service name might not be a valid GraphQL identifier? Are we doing some sort of cleanup somewhere? Also can we not call it "serviceName"? I thought we are trying to put that terminology aside in favor of "subgraph" as much as possible. Also, I am not going to super bikeshed this particular point too much, but it doesn't actually feel like the "subgraph name/service name" is a property of the "endpoint" (the server you're talking to) as much as it's a property of the subgraph itself, so it seems like it would go better on |
name: 'join__Graph', | ||
values: Object.fromEntries( | ||
serviceList.map((service) => [ | ||
service.name.toUpperCase(), |
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.
So specifically we might have to do something more special here (or at an earlier place in the process?) to ensure that these values are (a) valid GraphQL names and (b) are unique after upper-casing.
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 insight onto whether the proper fix if these fail should be reporting an error vs sanitizing/uniquifying. we should make sure though that if we uniquify that we do so in a deterministic fashion... though I guess these enum values technically don't need to be stable across composition runs, it would be nice if they were.)
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.
Funny, we talked about the uppercasing issue today. Though it didn't cross our mind that the serviceName
must now be a valid graphql name which is a great consideration. If we're going to go to the length of sanitizing/uniquifying, should we just generate the enum value itself with the knowledge that anything important is captured in the directive on it?
Suppose my services:
1products
, 2products
, etc.
We have to modify them, is there still a point in preserving some semblance of the original or...? I'm not sure, to be honest!
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 think having the enum values be something a human can look at in the rest of the schema and have associations with is valuable.
Frankly if we don't want to do sanitizing/uniquifying I'd rather we just go back to providing them as strings to the other directives rather than enums... But I think a light dusting of sanitization and uniquification would be reasonable. Something like:
1: Replace all non-alphanumerics with _
2: If it starts with a digit, prefix one _
3: (MAYBE, not sure this actually improves things) Upper-case all letters
4: If any string ends with an underscore followed by a string of digits (_[0-9]+$
), add another underscore at the end.
5: In case of conflicts, postfix all strings with another _
and a number. Choose the numbers based on the original ordering of the strings before all translations, 1-indexed.
What's the point of step 4? It's so that if you start with A
a
A_1
you end up with A_1
A_2
A_1_
rather than the third one colliding with the first one.
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, this is definitely more complicated than I thought it would be. But if we want to use graph names as enum values, we're going to need at least some variant of this algorithm. Uppercasing isn't strictly needed, but using uppercase for enum values is a strong convention in GraphQL, so it does make the SDL more humanly readable.
That's a good point. We still use
My concern with That would give us something like this:
|
@martijnwalraven and I have agreed to close this PR in favor of the https://github.com/apollographql/federation/compare/typescript-redux branch which has these changes merged and then some. We'll be collaborating on that branch. Leaving this open because of comments which might still need to be resolved. |
This commit re-introduces the TypeScript query planner, and adds support for core schema as input. This builds on the work @trevor-scheer has done in #588 to have composition output core schema instead of the previous incarnation of CSDL. We've verified the output of the query planner through a large number of tests and feel confident these changes shouldn't affect the generated query plans. @apollo/query-planner currently exposes the same API as before, but we'll change this in a follow-up PR to avoid repeated printing/parsing of schemas and queries. Any references to CSDL both internally and outward facing have been updated to their new counterpart: `supergraphSdl`. Supergraph SDL implements both the core and join specs, necessary for powering a router. Co-authored-by: Trevor Scheer <trevor.scheer@gmail.com> Co-authored-by: Jesse Rosenberger <git@jro.cc>
Superseded by (included in) #622 |
This commit re-introduces the TypeScript query planner, and adds support for core schema as input. This builds on the work @trevor-scheer has done in #588 to have composition output core schema instead of the previous incarnation of CSDL. We've verified the output of the query planner through a large number of tests and feel confident these changes shouldn't affect the generated query plans. @apollo/query-planner currently exposes the same API as before, but we'll change this in a follow-up PR to avoid repeated printing/parsing of schemas and queries. Any references to CSDL both internally and outward facing have been updated to their new counterpart: `supergraphSdl`. Supergraph SDL implements both the core and join specs, necessary for powering a router. Co-authored-by: Trevor Scheer <trevor.scheer@gmail.com> Co-authored-by: Jesse Rosenberger <git@jro.cc>
WIP
Review by commit for simplicity - Initial baseline commit should make reviewing the changes from CSDL -> Core simpler to think about.