-
Notifications
You must be signed in to change notification settings - Fork 254
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
Allows @shareable
to be repeatable (to allow it on both definition and extensions)
#2175
Allows @shareable
to be repeatable (to allow it on both definition and extensions)
#2175
Conversation
👷 Deploy request for apollo-federation-docs pending review.Visit the deploys page to approve it
|
This pull request is automatically built and testable in CodeSandbox. To see build info of the built libraries, click here or the icon next to each commit SHA. |
# This example only works with v2.2+ of the federation specification. In earlier versions, `@shareable` was | ||
# not marked `repeatable` and so this example would be rejected by graphQL. | ||
extend schema | ||
@link(url: "https://specs.apollo.dev/federation/v2.2", import: ["@shareable"]) |
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.
Side-note: I noticed that the document has plenty of @link
, and almost all of them still link to federation/v2.0
(the one exception is the part about @composeDirective
with links to federation/v2.1
because it has to). But I've been wondering if we shouldn't try to have the doc always link to the most recent spec version? I mean, we probably have to at the moment because studio composition is lagging behind, but this doesn't feel like we setup user for the best experience, so it might be worth discussing how we get to a better state. Anyway, certainly an aside...
@@ -133,12 +133,12 @@ directive @requires(fields: _FieldSet!) on FIELD_DEFINITION | |||
|
|||
directive @provides(fields: _FieldSet!) on FIELD_DEFINITION | |||
|
|||
directive @extends on OBJECT | INTERFACE |
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.
Note to reviewers: the reason the order of this changed is due to some code "consolidation" of the refactoring done in federationSpec.ts
/federation.ts
. More precisely, the completeFed1SubgraphSchema
method in federation.ts
used to manually list the directive, and it did so in an ever so slightly different order than how those same directives are listed in the fed2 spec. But the code now use the same list in both case (the legacyFederationDirectives
list in federationSpec.ts
) and that list use the existing order of the fed2 spec, but thus slightly change the order when we're "completing" a fed1 schema.
Now, we could of course avoid that order change here by recreating 2 separate lists, but I genuinely think the change of order here doesn't matter (and so is worth consolidating the code a bit). In particular, I don't think this would show up in "diffs" for users, because this here is the output of buildSubgraphSchema
, and that's only used by subgraph to generate a valid schema that can be run by Apollo Server, but it is not the schema that user upload to studio in particular (use upload the "input" of buildSubgraphSchema
).
e594195
to
9117c3a
Compare
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 couple of minor comments, but approved otherwise.
// Note that a previous test makes sure that _not_ having @shareable on the type extension ends up failing (as `b` is | ||
// not considered shareable in `subgraphA`. So succeeding here shows both that @shareable is accepted in the 2 places | ||
// (definition and extension) but also that it's properly taking into account. | ||
assertCompositionSuccess(result); |
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.
Did you want to assert anytyhing about the fields existing in the supergraph?
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 wasn't sure there wasn't much meaningful to test about fields in the supergraph since @shareable
is mostly about preventing composition in some cases, but results in nothing special in the supergraph.
directive @external(reason: String) on OBJECT | FIELD_DEFINITION | ||
|
||
directive @tag(name: String!) repeatable on FIELD_DEFINITION | OBJECT | INTERFACE | UNION | ARGUMENT_DEFINITION | SCALAR | ENUM | ENUM_VALUE | INPUT_OBJECT | INPUT_FIELD_DEFINITION | ||
|
||
directive @extends on OBJECT | INTERFACE |
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.
Why does this ordering change? Should we try to avoid if there isn't a good reason?
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.
See my comment made above. I explained why that happened and what is imo ok.
internals-js/src/federation.ts
Outdated
@@ -115,6 +106,7 @@ const FEDERATION_SPECIFIC_VALIDATION_RULES = [ | |||
|
|||
const FEDERATION_VALIDATION_RULES = specifiedSDLRules.filter(rule => !FEDERATION_OMITTED_VALIDATION_RULES.includes(rule)).concat(FEDERATION_SPECIFIC_VALIDATION_RULES); | |||
|
|||
const ALL_DEFAULT_FEDERATION_DIRECTIVE_NAMES = Object.values(FederationDirectiveName) as string[]; |
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.
Nit. Prefer:
const ALL_DEFAULT_FEDERATION_DIRECTIVE_NAMES: string[] = Object.values(FederationDirectiveName);
so you don't have to do any casting.
internals-js/src/federation.ts
Outdated
metadata: FederationMetadata, | ||
errorCollector: GraphQLError[], | ||
) { | ||
const shareableApplications: Directive<any, {}>[] = element.appliedDirectivesOf(metadata.shareableDirective()); |
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 there a reason you want Directive<any, {}>[]
instead of Directive[]
?
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 was more than the former was a bit more precise than the later (making it clear there is not argument to the applications), but certainly doesn't really change anything to that method so happy to shorten it.
without: Directive<any, {}>[], | ||
with: MultiMap<Extension<any>, Directive<any, {}>>, | ||
}; | ||
const byExtensions = shareableApplications.reduce<ByExtensions>( |
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.
Oh neat! Specifying the return type of reduce in the <> is much more readable.
But allow it only if applied to different "declaration" of a type, so typically both on the main type definition as well as on some of its extensions. Fixes apollographql#2135
9117c3a
to
ca9e2f4
Compare
The `@shareable` directive was always intended to be only on object types. However, its location being `FIELD_DEFINITION`, it is syntactically allowed on interface fields, and due to an oversight of the original code, doing so had not been propertly rejected (but it had no effects whatsoever). This oversight was noticed during apollographql#2175 and a validation was added to reject a `@shareable` on interface fields. Unfortunately, it appears a number of users had started putting such `@shareable` instances, so the added validation breaks composition for those users on upgrades (the "fix" for those user is trivial, just removing `@shareable` on any interface field, it did nothing and so will have no other impact). However, sticking with the status quo is highly undesirable: continuing to allow `@shareable` on interface fields but having it do nothing whatsoever is guaranteed to create confusion, and can even be harmful if a user is falsly led to believe that it does something it does not. So this patch explores a middle-ground alternative: it gives a meaning to `@shareable` on interface fields. That meaning is to force implementations to also mark the field as `@shareable`, as this feel like the most intuitive behaviour that can be done simply. Note that this is a middle ground in the sense that a subset of the users that had started using `@shareable` on interface fields may still see composition break on upgrades, if their implementation does not "manually inherit" those `@shareable` already. But experimentally, it appears that this patch avoids breaking for almost all the cases we have access to.
`@shareable` was never meant to be applied to interface fields, but before, and since apollographql#2175, it is correctly rejected. Before that, it was syntactically accepted but was doing nothing at all, as the code was entirely ignoring. This patch document the behaviour and change.
`@shareable` was never meant to be applied to interface fields, but before, and since #2175, it is correctly rejected. Before that, it was syntactically accepted but was doing nothing at all, as the code was entirely ignoring. This patch document the behaviour and change.
The rational this is explained on #2135 and in particular allows the example in this comment to work.
For backward compatibility concerns, the patch introduces a new version (
federation/v2.2
) of the federation spec and only make@shareable
repeatable in that version. This means that nothing changes for existing subgraphs on update and you have to "manually" change the subgraph to use the new version to be able to use this. More importantly, this means that subgraph libraries that continue to generate the previous version won't break in any way. They just may want to start generating the new version at some point in the future so their users can make use of the additional flexibility afforded by this change.On the upgrade side of things, probably stating the obvious, but if a subgraph does upgrade to use the new 2.2 version, then it needs to make sure to use a version of composition that knows it.
Regarding the code itself, marking
@shareable
repeatable is trivial, but more interesting changes are: