Skip to content
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

Fix merging of arguments #1567

Merged
merged 1 commit into from
Mar 7, 2022
Merged

Fix merging of arguments #1567

merged 1 commit into from
Mar 7, 2022

Conversation

pcmanus
Copy link
Contributor

@pcmanus pcmanus commented Mar 7, 2022

We shouldn't merge arguments unless they appear in all subgraphs, orthis lead to unclear semantic if those argument are passed by the end user.

I'll also note that the pre-existing behaviour, which was adding arguments to the supergraph if it appeared in any subgraph was potentially leading to errors when decoding the supergraph because we didn't kept information of where the argument was defined and as a result we sometimes inferred that an argument was in a subgraph that didn't have the argument type. Long story short, if we only include argument that are in all subgraphs (that have the field/directive definition), we solve that problem as well.

@pcmanus pcmanus requested a review from clenfest March 7, 2022 11:40
@codesandbox-ci
Copy link

codesandbox-ci bot commented Mar 7, 2022

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.

composition-js/src/merging/merge.ts Outdated Show resolved Hide resolved
composition-js/src/merging/merge.ts Show resolved Hide resolved
Copy link
Contributor

@benweatherman benweatherman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added some nitpicky comments, do with them what you will. Otherwise, things look great to me!

const arg = dest.addArgument(argName);
// If all the sources that have the field have the argument, we do merge it
// and we're good, but otherwise ...
if (!sources.every((s) => !s || s.argument(argName))) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpicky question Could this be changed to

Suggested change
if (!sources.every((s) => !s || s.argument(argName))) {
if (sources.some((s) => !s || s.argument(argName))) {

I think that's the same logic, but it's a little easier for my brain to understand that version.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. I initially had the if without the negation and then a else, but realised I didn't need the first part. Agreed avoiding the negation is probably better now. That said, to have the same logic I believe it needs to be:

if (sources.some((s) => s && !s.argument(argName))) {

Essentially, Not(For all x, P(x)) is It exists x, Not(P(x)).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, good catch on the logic. Makes sense to me if you wanna make that tweak.

// hint. But if it is mandatory, then we have to fail composition, otherwise
// the query planner would have no choice but to generate invalidate queries.
const nonOptionalSources = sources.map((s, i) => s && s.argument(argName)?.isRequired() ? this.names[i] : undefined).filter((s) => !!s) as string[];
if (nonOptionalSources.length > 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question/nitpick Do we normally compare against > 0?

Suggested change
if (nonOptionalSources.length > 0) {
if (nonOptionalSources.length) {

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I do :). Overall, I don't have a strong opinion but I'm not a fan of relying on implicit conversions too much (and to me this just reads better that way) and some googling seemed to imply there wasn't a huge consensus either way. Don't mind adapting though if there is a preference for the shorter pattern.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems fine to me, I don't feel strongly. I just wanna make sure I stay consistent when I do this sort of thing.

(Full disclosure: I'm normally in the "don't compare against 0" camp but 🤷 )

@@ -301,6 +301,11 @@ const INVALID_LINK_DIRECTIVE_USAGE = makeCodeDefinition(
'An application of the @link directive is invalid/does not respect the specification.'
);

const REQUIRED_ARGUMENT_MISSING_IN_SOME_SUBGRAPH = makeCodeDefinition(
'REQUIRED_ARGUMENT_MISSING_IN_SOME_SUBGRAPH',
'An argument (of a field or directive definition) is not defined in all the subgraphs defining the field and is mandatory in at least some of them.'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick I think this error message could be more clear (but that might be due to my newness)

Suggested change
'An argument (of a field or directive definition) is not defined in all the subgraphs defining the field and is mandatory in at least some of them.'
'An argument of a field or directive definition is mandatory in some subgraphs, but the argument is not defined in all subgraphs that define the field or directive definition.'

We shouldn't merge arguments unless they appear in all subgraphs, or
this lead to unclear semnantic if those argument are passed by the end
user.
@pcmanus pcmanus self-assigned this Mar 7, 2022
@pcmanus pcmanus merged commit 5e0436e into main Mar 7, 2022
@pcmanus pcmanus deleted the argument_merge branch March 7, 2022 17:07
pcmanus pushed a commit that referenced this pull request Mar 8, 2022
The patch from #1567 mistakenly broke then merging of graphQL built-in
directives (typically `@deprecated`). And unfortunately, we were missing
tests for it.

This commit fixes that issue and adds proper testing.

While adding tests, I also noticed that when merging repeatable
directives that have arguments, if the argument values didn't match
between subgraphs, we were failing composition (on account that
we don't know which of the arguments should "win"), but the error
returned was not useful to users. Or more precisely, merging wasn't
error out but instead was applying the non-repeatable directive multiple
times to the supergraph, and so upon validating the supergraph at
the end of merging, the user would get a "@x cannot appears multiple
times at this location", but that's not as actionnable as it should be.
The patch adds a better error for this (and a test for it).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants