Skip to content

Commit

Permalink
fix(federation/utils): merge selections for keys and computed fields …
Browse files Browse the repository at this point in the history
…correctly
  • Loading branch information
ardatan committed Aug 8, 2024
1 parent d0301e8 commit dbb0516
Show file tree
Hide file tree
Showing 4 changed files with 75 additions and 6 deletions.
21 changes: 21 additions & 0 deletions .changeset/curvy-flowers-play.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
---
'@graphql-tools/federation': patch
---

If there are repeated computed fields like below, project the data for the computed fields for each `fields` and merge them correctly.
And if they are array as in `userOrders`, merge them by respecting the order (the second one can have `price` maybe).

```graphql
type UserOrder @key(fields: "id") {
id: ID!
status: String!
price: Int!
}

type User @key(fields: "id") {
id: ID!
userOrders: [UserOrder!] @external
totalOrdersPrices: Int @requires(fields: "userOrders { id }")
aggregatedOrdersByStatus: Int @requires(fields: "userOrders { id }")
}
```
4 changes: 2 additions & 2 deletions packages/federation/src/supergraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -658,11 +658,11 @@ export function getStitchingOptionsFromSupergraphSdl(
for (const [typeName, keys] of typeNameKeyMap) {
const mergedTypeConfig: MergedTypeConfig = (mergeConfig[typeName] = {});
const fieldsKeyMap = typeNameFieldsKeyMap?.get(typeName);
const extraKeys: string[] = [];
const extraKeys = new Set<string>();
if (fieldsKeyMap) {
const fieldsConfig: Record<string, MergedFieldConfig> = (mergedTypeConfig.fields = {});
for (const [fieldName, fieldNameKey] of fieldsKeyMap) {
extraKeys.push(fieldNameKey);
extraKeys.add(fieldNameKey);
fieldsConfig[fieldName] = {
selectionSet: `{ ${fieldNameKey} }`,
computed: true,
Expand Down
18 changes: 14 additions & 4 deletions packages/federation/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,17 @@ export function projectDataSelectionSet(data: any, selectionSet?: SelectionSetNo
for (const selection of selectionSet.selections) {
if (selection.kind === Kind.FIELD) {
const key = selection.name.value;
if (data.hasOwnProperty(key)) {
if (Object.prototype.hasOwnProperty.call(data, key)) {
const projectedKeyData = projectDataSelectionSet(data[key], selection.selectionSet);
if (projectedData[key]) {
projectedData[key] = mergeDeep([projectedData[key], projectedKeyData]);
projectedData[key] = mergeDeep(
[projectedData[key], projectedKeyData],
undefined,
true,
true,
);
} else {
projectedData[key] = projectDataSelectionSet(data[key], selection.selectionSet);
projectedData[key] = projectedKeyData;
}
}
} else if (selection.kind === Kind.INLINE_FRAGMENT) {
Expand All @@ -40,7 +45,12 @@ export function projectDataSelectionSet(data: any, selectionSet?: SelectionSetNo
}
Object.assign(
projectedData,
mergeDeep([projectedData, projectDataSelectionSet(data, selection.selectionSet)]),
mergeDeep(
[projectedData, projectDataSelectionSet(data, selection.selectionSet)],
undefined,
true,
true,
),
);
}
}
Expand Down
38 changes: 38 additions & 0 deletions packages/federation/test/getKeyFnForFederation.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,42 @@ describe('getKeyFnForFederation', () => {
price: 100,
});
});
it('with repeating fields returning arrays', () => {
const keys = ['userOrders { id }', 'userOrders { tag }'];
const data = {
__typename: 'User',
id: 1,
name: 'Test',
userOrders: [
{
__typename: 'UserOrder',
id: '1',
total: 100,
tag: 'Test1',
},
{
__typename: 'UserOrder',
id: '2',
total: 400,
tag: 'Test2',
},
],
};
const keyFn = getKeyFnForFederation('User', keys);
expect(keyFn(data)).toEqual({
__typename: 'User',
userOrders: [
{
__typename: 'UserOrder',
id: '1',
tag: 'Test1',
},
{
__typename: 'UserOrder',
id: '2',
tag: 'Test2',
},
],
});
});
});

0 comments on commit dbb0516

Please sign in to comment.