Skip to content

Commit

Permalink
fix(gateway): maintain immutability when merging selection sets (apol…
Browse files Browse the repository at this point in the history
  • Loading branch information
jakeblaxon committed Jun 29, 2020
1 parent e4341f7 commit de90953
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 6 deletions.
1 change: 1 addition & 0 deletions packages/apollo-gateway/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
> The changes noted within this `vNEXT` section have not been released yet. New PRs and commits which introduce changes should include an entry in this `vNEXT` section as part of their development. When a release is being prepared, a new header will be (manually) created below and the appropriate changes within that release will be moved into the new section.
- The default branch of the repository has been changed to `main`. As this changed a number of references in the repository's `package.json` and `README.md` files (e.g., for badges, links, etc.), this necessitates a release to publish those changes to npm. [PR #4302](https://github.com/apollographql/apollo-server/pull/4302)
- __FIX__: The `mergeFieldNodeSelectionSets` method no longer mutates original FieldNode objects. Before, it was updating the selection set of the original object, corrupting the data accross requests.

## 0.16.9

Expand Down
13 changes: 8 additions & 5 deletions packages/apollo-gateway/src/FieldSet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,16 @@ function mergeFieldNodeSelectionSets(
nonAliasedFieldNodes,
).values(),
).map((nodesWithSameName) => {
const node = nodesWithSameName[0];
const node = { ...nodesWithSameName[0] };
if (node.selectionSet) {
node.selectionSet.selections = mergeFieldNodeSelectionSets(
nodesWithSameName.flatMap(
(node) => node.selectionSet?.selections || [],
node.selectionSet = {
...node.selectionSet,
selections: mergeFieldNodeSelectionSets(
nodesWithSameName.flatMap(
(node) => node.selectionSet?.selections || [],
),
),
);
};
}
return node;
});
Expand Down
115 changes: 115 additions & 0 deletions packages/apollo-gateway/src/__tests__/gateway/queryPlanCache.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,118 @@ it('supports multiple operations and operationName', async () => {
],
});
});

it('does not corrupt cached queryplan data across requests', async () => {
const serviceA = {
name: 'a',
typeDefs: gql`
type Query {
user: User
}
type User @key(fields: "id") {
id: ID!
preferences: Preferences
}
type Preferences {
favorites: Things
}
type Things {
color: String
animal: String
}
`,
resolvers: {
Query: {
user() {
return {
id: '1',
preferences: {
favorites: { color: 'limegreen', animal: 'platypus' },
},
};
},
},
},
};

const serviceB = {
name: 'b',
typeDefs: gql`
extend type User @key(fields: "id") {
id: ID! @external
preferences: Preferences @external
favoriteColor: String
@requires(fields: "preferences { favorites { color } }")
favoriteAnimal: String
@requires(fields: "preferences { favorites { animal } }")
}
extend type Preferences {
favorites: Things @external
}
extend type Things {
color: String @external
animal: String @external
}
`,
resolvers: {
User: {
favoriteColor(user: any) {
return user.preferences.favorites.color;
},
favoriteAnimal(user: any) {
return user.preferences.favorites.animal;
},
},
},
};

const gateway = new ApolloGateway({
localServiceList: [serviceA, serviceB],
buildService: service => {
return new LocalGraphQLDataSource(buildFederatedSchema([service]));
},
});

const { schema, executor } = await gateway.load();

const server = new ApolloServer({ schema, executor });

const call = createTestClient(server);

const query1 = `#graphql
query UserFavoriteColor {
user {
favoriteColor
}
}
`;

const query2 = `#graphql
query UserFavorites {
user {
favoriteColor
favoriteAnimal
}
}
`;

const result1 = await call.query({
query: query1,
});
const result2 = await call.query({
query: query2,
});
const result3 = await call.query({
query: query1,
});

expect(result1.errors).toEqual(undefined);
expect(result2.errors).toEqual(undefined);
expect(result3.errors).toEqual(undefined);
expect(result1).toEqual(result3);
});
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,6 @@ it('collapses nested requires with user-defined fragments', async () => {
preferences {
favorites {
animal
color
}
}
}
Expand Down

0 comments on commit de90953

Please sign in to comment.