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

Add locations to composition errors #686

Merged

Conversation

mayakoneval
Copy link
Contributor

@mayakoneval mayakoneval commented Apr 22, 2021

This PR

  • adds location information to composition errors that are specific to federation. This includes pre, post and composition errors.
  • There are a couple things that needed to be in place for location information to be exposed properly. The gql function strips loc information from the type definitions that it returns, so all tests that I edited use parse instead of gql.
  • Composition errors use a function errorWithCode which calls the GraphQLError constructor. If node is provided to the GraphQLError constructor with a loc field on node with a source on loc, the location information added to an error is the same as the location information on node. This took me a while to get :)
  • This PR is organized by error. Each commit is a composition error code as defined in the federation docs.
  • The first commit moves a couple tests from composeAndValidate into the describe('error because that seemed more fitting 🤷 I would be fine deleting this one if anyone disagrees.
  • I updated all the error code specific tests that I could find, as well as adding locations to composeAndValidate.test.ts and compose.test.ts

I recommend reviewing by commit

Please let me know of any concerns switching gql to parse.

Questions

  • Should I change all gql calls to parse calls in the test files I edited? I only changed the tests that checked the snapshot of the whole error, rather than the length of errors, since those are the ones that would check for location.

2. make sure all test's use parse() instead of gql() so that the loc fields are populated on each type definition
…Code to get the location & update tests

need to figure out why the tests don't print the errors as I expect
… errorWithCode to get locations & update tests
…ph nodes = locations for both subgraphs in location. updated tests
…hCodes = locations correspond to both services. updated tests
@mayakoneval mayakoneval force-pushed the maya/composition-errors-locations branch from bc8e27a to fcb15c4 Compare May 5, 2021 21:33
@mayakoneval mayakoneval force-pushed the maya/composition-errors-locations branch from b512dbc to 4a94b78 Compare May 5, 2021 22:01
Comment on lines 62 to 65
extendingServiceTypeNode && 'directives' in extendingServiceTypeNode ?
extendingServiceTypeNode.directives?.find(directive =>
directive.name.value === 'key')?.arguments?.find
(argument => argument.name.value === 'fields') : undefined,
Copy link
Member

Choose a reason for hiding this comment

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

I think the nodes we want in this case are each of the @key directives on the extended type?

Copy link
Contributor Author

@mayakoneval mayakoneval May 7, 2021

Choose a reason for hiding this comment

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

For this one, since we can't select the general "directive area" I am going to select the type node, so the error can be located to the whole error where there are too many @keys.

@mayakoneval
Copy link
Contributor Author

mayakoneval commented May 7, 2021

Okay I spent time today doing a full search through all the codes to make sure the locations make sense, which meant a few more changes than were requested. I'm hoping this looks pretty good 😋 . Excited about this! @trevor-scheer

@mayakoneval mayakoneval requested a review from trevor-scheer May 7, 2021 02:14
| FieldDefinitionNode
| SchemaDefinitionNode
| FieldNode
| DefinitionNode
Copy link
Member

Choose a reason for hiding this comment

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

I like the direction you're taking this. I will suggest, I think we should either go full bore Maybe<ASTNode> as the type or only permit types which at least have a directives property on them. I like the latter, since the typings will warn users of the fn when they're using it inappropriately. In your case, the only inappropriate usage which is now included in this union is the DirectiveDefinitonNode, so I'll suggest this (+ a nice little TS revamp since we're here)

export function findDirectivesOnNode(
  node: Maybe<
    Exclude<
      | FieldDefinitionNode
      | InputValueDefinitionNode
      | FieldNode
      | DefinitionNode,
      DirectiveDefinitionNode
    >
  >,
  directiveName: string,
) {
  return (
    node?.directives?.filter(
      (directive) => directive.name.value === directiveName,
    ) ?? []
  );
}

Copy link
Contributor Author

@mayakoneval mayakoneval May 7, 2021

Choose a reason for hiding this comment

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

Got it, I think I'll go with enumerating all the AST type with directives if that sounds good, rather than excluding from the ASTNode, since there aren't actually too many Types with directives.

export function findSelectionSetOnNode(
node: Maybe<
| FieldDefinitionNode
| DefinitionNode
Copy link
Member

Choose a reason for hiding this comment

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

The same Exclude<> type I suggested above would give you a similar ergonomic win here.

? node.directives.filter(
directive => directive.name.value === directiveName,
)
: [];
}

export function findSelectionSetOnNode(
Copy link
Member

Choose a reason for hiding this comment

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

This fn is a bit tough to grok. Can you please add a good description to what this fn looks for and how?

Comment on lines 87 to 95
return node && 'directives' in node && node.directives
? node.directives.find(
directive =>
directive.name.value === directiveName && directive.arguments?.some(
argument => argument.value.kind === "StringValue" &&
argument.value.value.includes(elementInSelectionSet)
))?.arguments?.find(
argument => argument.name.value === 'fields')?.value
: undefined;
Copy link
Member

@trevor-scheer trevor-scheer May 7, 2021

Choose a reason for hiding this comment

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

  1. we should use Kind.STRING for "StringValue" (or use the isStringValueNode predicate fn!)
  2. I'm not totally sure I understand this correctly but I think I do. Is the includes here possibly error-prone?

I'm imagining 2 keys:
@key(fields: "id")
@key(fields: "sku { id }")

Could this scenario break this implementation? I might be mistaken and just need to understand this a bit better.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In the scenario you described:

type SomeType @key(fields: "id") @key(fields: "sku { id }") { and we are looking for the field with id on it, yeah this would break. If this were the case, and we wanted to find a top level field, would it be sufficient to make sure that string is not wrapped in curly braces? I'll write a test for this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The resolution here was to search for the entire selection set, so we know we are selecting the correct key.

…single field. Update test name to be plural & write definition for selection set search
@mayakoneval mayakoneval force-pushed the maya/composition-errors-locations branch from 0fc8d54 to 0b5a188 Compare May 7, 2021 19:19
@mayakoneval mayakoneval requested a review from trevor-scheer May 7, 2021 21:26
argument => argument.value.kind === "StringValue" &&
argument.value.value.includes(elementInSelectionSet)
argument => isStringValueNode(argument.value) &&
argument.value.value.includes(printedSelectionSet)
Copy link
Member

Choose a reason for hiding this comment

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

Can this be === now instead of includes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

yes!

Copy link
Member

@trevor-scheer trevor-scheer left a comment

Choose a reason for hiding this comment

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

Ship it!

Can we also replace the printFieldSet fn in printSupergraphSdl with the one we've moved into the utils file?

@mayakoneval
Copy link
Contributor Author

@trevor-scheer just pushed those last changes, I think you have to merge :) Woohoo!

@mayakoneval mayakoneval force-pushed the maya/composition-errors-locations branch from 01642f6 to 46bd00c Compare May 10, 2021 17:49
@mayakoneval mayakoneval force-pushed the maya/composition-errors-locations branch from 46bd00c to 606a341 Compare May 10, 2021 17:54
@mayakoneval
Copy link
Contributor Author

cargo test on the rust windows test has failed twice, I force pushed again to try.

@trevor-scheer
Copy link
Member

@mayakoneval this is probably not a you problem - I'll take a look at the errors in a bit. You might also try merging in main to make sure your branch is fully up to date.

@trevor-scheer
Copy link
Member

@mayakoneval I merged main which must've introduced some good TS changes since I then had to remove a couple unused imports. Now everything is passing, so I'm going to land this now 🎉

@trevor-scheer trevor-scheer merged commit 3e225b5 into apollographql:main May 10, 2021
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.

4 participants