Skip to content

Commit

Permalink
Fix aliased shared root fields (#6619)
Browse files Browse the repository at this point in the history
* chore: add failing tests

* Fix and isolated test

* Remove unused import

* Changeset

---------

Co-authored-by: Tomas Kroupa <[email protected]>
Co-authored-by: Arda TANRIKULU <[email protected]>
  • Loading branch information
3 people authored Oct 31, 2024
1 parent 17bfe64 commit e9906eb
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 2 deletions.
6 changes: 6 additions & 0 deletions .changeset/tricky-jars-wink.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@graphql-tools/delegate': minor
'@graphql-tools/federation': patch
---

Handle shared root field queries with aliases
1 change: 1 addition & 0 deletions packages/delegate/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ export * from './subschemaConfig.js';
export * from './types.js';
export * from './extractUnavailableFields.js';
export * from './leftOver.js';
export * from './symbols.js';
19 changes: 17 additions & 2 deletions packages/federation/src/supergraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@ import {
BatchingOptions,
delegateToSchema,
extractUnavailableFieldsFromSelectionSet,
isExternalObject,
MergedFieldConfig,
MergedTypeConfig,
SubschemaConfig,
subtractSelectionSets,
Transform,
UNPATHED_ERRORS_SYMBOL,
} from '@graphql-tools/delegate';
import { buildHTTPExecutor, HTTPExecutorOptions } from '@graphql-tools/executor-http';
import {
Expand Down Expand Up @@ -1320,6 +1322,13 @@ const entitiesFieldDefinitionNode: FieldDefinitionNode = {

const specifiedTypeNames = ['ID', 'String', 'Float', 'Int', 'Boolean', '_Any', '_Entity'];

function makeExternalObject(data: unknown, errors: Error[]) {

Check failure on line 1325 in packages/federation/src/supergraph.ts

View workflow job for this annotation

GitHub Actions / deployment

'errors' is declared but its value is never read.

Check failure on line 1325 in packages/federation/src/supergraph.ts

View workflow job for this annotation

GitHub Actions / Type Check on GraphQL v15

'errors' is declared but its value is never read.

Check failure on line 1325 in packages/federation/src/supergraph.ts

View workflow job for this annotation

GitHub Actions / Type Check on GraphQL v16

'errors' is declared but its value is never read.

Check failure on line 1325 in packages/federation/src/supergraph.ts

View workflow job for this annotation

GitHub Actions / Type Check on GraphQL v17.0.0-alpha.1

'errors' is declared but its value is never read.

Check failure on line 1325 in packages/federation/src/supergraph.ts

View workflow job for this annotation

GitHub Actions / ESM Test

'errors' is declared but its value is never read.

Check failure on line 1325 in packages/federation/src/supergraph.ts

View workflow job for this annotation

GitHub Actions / Browser Test

'errors' is declared but its value is never read.
if (!isExternalObject(data) && typeof data === 'object' && data != null) {
data[UNPATHED_ERRORS_SYMBOL] = [];
}
return data;
}

function mergeResults(results: unknown[]) {
const errors: Error[] = [];
const datas: unknown[] = [];
Expand All @@ -1328,14 +1337,20 @@ function mergeResults(results: unknown[]) {
errors.push(...result.errors);
} else if (result instanceof Error) {
errors.push(result);
} else {
} else if (result != null) {
datas.push(result);
}
}
if (datas.length) {
return mergeDeep(datas, false, true, true);
if (datas.length === 1) {
return makeExternalObject(datas[0], errors);
}
return makeExternalObject(mergeDeep(datas), errors);
}
if (errors.length) {
if (errors.length === 1) {
throw errors[0];
}
return new AggregateError(errors, errors.map(error => error.message).join(', \n'));
}
return null;
Expand Down
111 changes: 111 additions & 0 deletions packages/federation/test/aliased-shared-root.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { parse } from 'graphql';
import { buildSubgraphSchema } from '@apollo/subgraph';
import { normalizedExecutor } from '@graphql-tools/executor';
import { getStitchedSchemaFromLocalSchemas } from './getStitchedSchemaFromLocalSchemas';

describe('Aliased Shared Root Fields', () => {
it('issue #6613', async () => {
const query = /* GraphQL */ `
query {
testNestedField {
subgraph1 {
id
email
sub1
}
testUserAlias: subgraph2 {
id
email
sub2
}
}
}
`;

const expectedResult = {
data: {
testNestedField: {
subgraph1: {
id: 'user1',
email: '[email protected]',
sub1: true,
},
testUserAlias: {
id: 'user2',
email: '[email protected]',
sub2: true,
},
},
},
};

const subgraph1 = buildSubgraphSchema({
typeDefs: parse(/* GraphQL */ `
type Query {
testNestedField: TestNestedField
}
type TestNestedField {
subgraph1: TestUser1!
}
type TestUser1 {
id: String!
email: String!
sub1: Boolean!
}
`),
resolvers: {
Query: {
testNestedField: () => ({
subgraph1: () => ({
id: 'user1',
email: '[email protected]',
sub1: true,
}),
}),
},
},
});
const subgraph2 = buildSubgraphSchema({
typeDefs: parse(/* GraphQL */ `
type Query {
testNestedField: TestNestedField
}
type TestNestedField {
subgraph2: TestUser2!
}
type TestUser2 {
id: String!
email: String!
sub2: Boolean!
}
`),
resolvers: {
Query: {
testNestedField: () => ({
subgraph2: () => ({
id: 'user2',
email: '[email protected]',
sub2: true,
}),
}),
},
},
});

const gatewaySchema = await getStitchedSchemaFromLocalSchemas({
subgraph1,
subgraph2,
});

const result = await normalizedExecutor({
schema: gatewaySchema,
document: parse(query),
});

expect(result).toEqual(expectedResult);
});
});

0 comments on commit e9906eb

Please sign in to comment.