Skip to content

Commit

Permalink
Fix issue where selection set flattening uses the wrong parent type (#…
Browse files Browse the repository at this point in the history
…8664)

* Add failing test

* Fix issue where selection set flattening uses the wrong parent type

* Add changeset
  • Loading branch information
jdmoody committed Dec 1, 2022
1 parent eeecb48 commit 62f6554
Show file tree
Hide file tree
Showing 4 changed files with 95 additions and 4 deletions.
6 changes: 6 additions & 0 deletions .changeset/warm-trainers-shout.md
@@ -0,0 +1,6 @@
---
'@graphql-codegen/visitor-plugin-common': patch
'@graphql-codegen/typescript-operations': patch
---

Fix issue where selection set flattening uses the wrong parent type
Expand Up @@ -248,7 +248,8 @@ export class SelectionSetToObject<Config extends ParsedDocumentsConfig = ParsedD
}

protected flattenSelectionSet(
selections: ReadonlyArray<SelectionNode>
selections: ReadonlyArray<SelectionNode>,
parentSchemaType?: GraphQLObjectType<any, any>
): Map<string, Array<SelectionNode | FragmentSpreadUsage>> {
const selectionNodesByTypeName = new Map<string, Array<SelectionNode | FragmentSpreadUsage>>();
const inlineFragmentSelections: InlineFragmentNode[] = [];
Expand All @@ -270,10 +271,16 @@ export class SelectionSetToObject<Config extends ParsedDocumentsConfig = ParsedD
}

if (fieldNodes.length) {
inlineFragmentSelections.push(this._createInlineFragmentForFieldNodes(this._parentSchemaType, fieldNodes));
inlineFragmentSelections.push(
this._createInlineFragmentForFieldNodes(parentSchemaType ?? this._parentSchemaType, fieldNodes)
);
}

this._collectInlineFragments(this._parentSchemaType, inlineFragmentSelections, selectionNodesByTypeName);
this._collectInlineFragments(
parentSchemaType ?? this._parentSchemaType,
inlineFragmentSelections,
selectionNodesByTypeName
);
const fragmentsUsage = this.buildFragmentSpreadsUsage(fragmentSpreads);

for (const [typeName, records] of Object.entries(fragmentsUsage)) {
Expand Down Expand Up @@ -513,7 +520,7 @@ export class SelectionSetToObject<Config extends ParsedDocumentsConfig = ParsedD
fragmentType.getTypes().find(objectType => objectType.name === parentSchemaType.name))
) {
// also process fields from fragment that apply for this parentType
const flatten = this.flattenSelectionSet(selectionNode.selectionNodes);
const flatten = this.flattenSelectionSet(selectionNode.selectionNodes, parentSchemaType);
const typeNodes = flatten.get(parentSchemaType.name) ?? [];
selectionNodes.push(...typeNodes);
for (const iinterface of parentSchemaType.getInterfaces()) {
Expand Down
Expand Up @@ -133,6 +133,20 @@ function test(q: QQuery) {
}"
`;
exports[`TypeScript Operations Plugin Issues #6874 - generates types when parent type differs from spread fragment member types and preResolveTypes=true 1`] = `
"export type SnakeQueryQueryVariables = Exact<{ [key: string]: never; }>;
export type SnakeQueryQuery = { __typename?: 'Query', snake: { __typename?: 'Snake', name: string, features: { __typename?: 'SnakeFeatures', color: string, length: number } } | { __typename?: 'Error' } };
type AnimalFragment_Bat_Fragment = { __typename?: 'Bat', features: { __typename?: 'BatFeatures', color: string, wingspan: number } };
type AnimalFragment_Snake_Fragment = { __typename?: 'Snake', features: { __typename?: 'SnakeFeatures', color: string, length: number } };
export type AnimalFragmentFragment = AnimalFragment_Bat_Fragment | AnimalFragment_Snake_Fragment;
"
`;
exports[`TypeScript Operations Plugin Selection Set Should generate the correct __typename when using both inline fragment and spread over type 1`] = `
"export type UserQueryQueryVariables = Exact<{ [key: string]: never; }>;
Expand Down
64 changes: 64 additions & 0 deletions packages/plugins/typescript/operations/tests/ts-documents.spec.ts
Expand Up @@ -5881,6 +5881,70 @@ function test(q: GetEntityBrandDataQuery): void {
"
`);
});

it('#6874 - generates types when parent type differs from spread fragment member types and preResolveTypes=true', async () => {
const testSchema = buildSchema(/* GraphQL */ `
interface Animal {
name: String!
}
type Bat implements Animal {
name: String!
features: BatFeatures!
}
type BatFeatures {
color: String!
wingspan: Int!
}
type Snake implements Animal {
name: String!
features: SnakeFeatures!
}
type SnakeFeatures {
color: String!
length: Int!
}
type Error {
message: String!
}
union SnakeResult = Snake | Error
type Query {
snake: SnakeResult!
}
`);

const query = parse(/* GraphQL */ `
query SnakeQuery {
snake {
... on Snake {
name
...AnimalFragment
}
}
}
fragment AnimalFragment on Animal {
... on Bat {
features {
color
wingspan
}
}
... on Snake {
features {
color
length
}
}
}
`);

const config = { preResolveTypes: true };

const { content } = await plugin(testSchema, [{ location: '', document: query }], config, {
outputFile: 'graphql.ts',
});

expect(content).toMatchSnapshot();
});
});

describe('conditional directives handling', () => {
Expand Down

0 comments on commit 62f6554

Please sign in to comment.