Skip to content

Commit c6c61bd

Browse files
author
Sylvain Lebresne
committed
Improve error handling on the supergraph
Some errors might be thrown while extracting subgraphs from the supergraph but without being handled gracefully, resulting in messages to the user that were less useful than they should be.
1 parent 89833ff commit c6c61bd

File tree

2 files changed

+80
-4
lines changed

2 files changed

+80
-4
lines changed

internals-js/src/__tests__/extractSubgraphsFromSupergraph.test.ts

+64
Original file line numberDiff line numberDiff line change
@@ -533,3 +533,67 @@ test('handles unions types having no members in a subgraph correctly', () => {
533533
expect(b.type('C')).toBeUndefined();
534534
expect(a.type('D')).toBeDefined();
535535
})
536+
537+
test('throw meaningful error for invalid federation directive fieldSet', () => {
538+
const supergraph = `
539+
schema
540+
@core(feature: "https://specs.apollo.dev/core/v0.2"),
541+
@core(feature: "https://specs.apollo.dev/join/v0.1", for: EXECUTION)
542+
{
543+
query: Query
544+
}
545+
546+
directive @core(as: String, feature: String!, for: core__Purpose) repeatable on SCHEMA
547+
548+
directive @join__field(graph: join__Graph, provides: join__FieldSet, requires: join__FieldSet) on FIELD_DEFINITION
549+
550+
directive @join__graph(name: String!, url: String!) on ENUM_VALUE
551+
552+
directive @join__owner(graph: join__Graph!) on INTERFACE | OBJECT
553+
554+
directive @join__type(graph: join__Graph!, key: join__FieldSet) repeatable on INTERFACE | OBJECT
555+
556+
type A @join__owner(graph: SERVICEA) @join__type(graph: SERVICEA, key: "id") @join__type(graph: SERVICEB, key: "id") {
557+
id: ID
558+
a: Int @join__field(graph: SERVICEB, requires: "b { x }")
559+
b: B
560+
}
561+
562+
type B @join__owner(graph: SERVICEA) @join__type(graph: SERVICEA, key: "id") {
563+
id: ID
564+
x: Int
565+
}
566+
567+
type Query {
568+
q: A
569+
}
570+
571+
enum core__Purpose {
572+
"""
573+
\`EXECUTION\` features provide metadata necessary to for operation execution.
574+
"""
575+
EXECUTION
576+
577+
"""
578+
\`SECURITY\` features provide metadata necessary to securely resolve fields.
579+
"""
580+
SECURITY
581+
}
582+
583+
scalar join__FieldSet
584+
585+
enum join__Graph {
586+
SERVICEA @join__graph(name: "serviceA" url: "")
587+
SERVICEB @join__graph(name: "serviceB" url: "")
588+
}
589+
`;
590+
591+
const schema = buildSupergraphSchema(supergraph)[0];
592+
expect(() => extractSubgraphsFromSupergraph(schema)).toThrow(
593+
'Error extracting subgraph "serviceB" from the supergraph: this might due to errors in subgraphs that were mistakenly ignored by federation 0.x versions but are rejected by federation 2.\n'
594+
+ 'Please try composing your subgraphs with federation 2: this should help precisely pinpoint the problems and, once fixed, generate a correct federation 2 supergraph.\n'
595+
+ '\n'
596+
+ 'Details:\n'
597+
+ '[serviceB] On field "A.a", for @requires(fields: "b { x }"): Cannot query field "b" on type "A" (if the field is defined in another subgraph, you need to add it to this subgraph with @external).'
598+
);
599+
})

internals-js/src/extractSubgraphsFromSupergraph.ts

+16-4
Original file line numberDiff line numberDiff line change
@@ -292,11 +292,11 @@ export function extractSubgraphsFromSupergraph(supergraph: Schema): Subgraphs {
292292
if (isFed1) {
293293
// Note that this could be a bug with the code handling fed1 as well, but it's more helpful to ask users to recompose their subgraphs with fed2 as either
294294
// it'll solve the issue and that's good, or we'll hit the other message anyway.
295-
const msg = `Error extracting subgraph ${subgraph.name} from the supergraph: this might due to errors in subgraphs that were mistakenly ignored by federation 0.x versions but are rejected by federation 2.\n`
296-
+ 'Please try composing your subgraphs with federation 2: this should help precisely pinpoint the errors and generate a correct federation 2 supergraph.';
295+
const msg = `Error extracting subgraph "${subgraph.name}" from the supergraph: this might due to errors in subgraphs that were mistakenly ignored by federation 0.x versions but are rejected by federation 2.\n`
296+
+ 'Please try composing your subgraphs with federation 2: this should help precisely pinpoint the problems and, once fixed, generate a correct federation 2 supergraph';
297297
throw new Error(`${msg}.\n\nDetails:\n${errorToString(e)}`);
298298
} else {
299-
const msg = `Unexpected error extracting subgraph ${subgraph.name} from the supergraph: this is either a bug, or the supergraph has been corrupted.`;
299+
const msg = `Unexpected error extracting subgraph ${subgraph.name} from the supergraph: this is either a bug, or the supergraph has been corrupted`;
300300
const dumpMsg = maybeDumpSubgraphSchema(subgraph);
301301
throw new Error(`${msg}.\n\nDetails:\n${errorToString(e)}\n\n${dumpMsg}`);
302302
}
@@ -479,7 +479,19 @@ function addExternalFieldsFromDirectiveFieldSet(
479479
}
480480
return created;
481481
};
482-
parseFieldSetArgument({parentType, directive, fieldAccessor});
482+
try {
483+
parseFieldSetArgument({parentType, directive, fieldAccessor, validate: false});
484+
} catch (e) {
485+
// Ignored on purpose: for fed1 supergraphs, it's possible that some of the fields defined in a federation directive
486+
// was _not_ defined in the subgraph because fed1 was not validating this properly (the validation wasn't handling
487+
// nested fields as it should), which may result in an error when trying to add those as an external field.
488+
// However, this is not the right place to throw. Instead, we ignore the problem and thus exit without having added
489+
// all the necessary fields, and so this very same directive will fail validation at the end of the extraction when
490+
// we do the final validation of the extracted subgraph (see end of `extractSubgraphsFromSupergraph`). And we prefer
491+
// failing then because 1) that later validation will collect all errors instead of failing on the first one and
492+
// 2) we already have special error messages and the ability to dump the extracted subgraphs for debug at that point,
493+
// so it's a much better place.
494+
}
483495
}
484496

485497
function addExternalFieldsFromInterface(metadata: FederationMetadata, type: ObjectType | InterfaceType) {

0 commit comments

Comments
 (0)