Skip to content

Commit

Permalink
fix: excluded the __typename from the root node of subscriptions when…
Browse files Browse the repository at this point in the history
… using addTypenameSelectionDocumentTransform with documentTransforms (#9889)

* Excluded the __typename from the root node of subscriptions

The __typename should not be added to the root node of a subscription since a single root node is expected and the code generator fails because of that

* Added the changeset

* Test for the fix for issue #9889

* Lint fix

* No need for capturing the variable
  • Loading branch information
Sojaner committed Mar 27, 2024
1 parent 313e082 commit cd60e14
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 4 deletions.
5 changes: 5 additions & 0 deletions .changeset/light-clocks-rhyme.md
@@ -0,0 +1,5 @@
---
'@graphql-codegen/client-preset': minor
---

The \_\_typename should not be added to the root node of a subscription when using addTypenameSelectionDocumentTransform with documentTransforms since a single root node is expected and the code generator fails because of that (refer to https://spec.graphql.org/draft/#sec-Single-root-field)
@@ -1,8 +1,8 @@
import { Kind, visit } from 'graphql';
import { ASTNode, OperationDefinitionNode, Kind, visit } from 'graphql';
import { Types } from '@graphql-codegen/plugin-helpers';

/**
* Automatically adds `__typename` selections to every object type in your GraphQL document.
* Automatically adds `__typename` selections to every object type in your GraphQL document except the root node in subscriptions since a single root field is expected (https://spec.graphql.org/draft/#sec-Single-root-field).
* This is useful for GraphQL Clients such as Apollo Client or urql that need typename information for their cache to function.
*/
export const addTypenameSelectionDocumentTransform: Types.DocumentTransformObject = {
Expand All @@ -11,8 +11,13 @@ export const addTypenameSelectionDocumentTransform: Types.DocumentTransformObjec
...document,
document: document.document
? visit(document.document, {
SelectionSet(node) {
SelectionSet(node, _, parent) {
const isSubscriptionRoot =
typeof (parent as ASTNode)?.kind === 'string' &&
(parent as ASTNode).kind === 'OperationDefinition' &&
(parent as OperationDefinitionNode).operation === 'subscription';
if (
!isSubscriptionRoot &&
!node.selections.find(selection => selection.kind === 'Field' && selection.name.value === '__typename')
) {
return {
Expand Down
120 changes: 119 additions & 1 deletion packages/presets/client/tests/client-preset.spec.ts
Expand Up @@ -3,7 +3,7 @@ import path from 'path';
import { executeCodegen } from '@graphql-codegen/cli';
import { mergeOutputs } from '@graphql-codegen/plugin-helpers';
import { validateTs } from '@graphql-codegen/testing';
import { preset } from '../src/index.js';
import { addTypenameSelectionDocumentTransform, preset } from '../src/index.js';
import { print } from 'graphql';

describe('client-preset', () => {
Expand Down Expand Up @@ -2513,5 +2513,123 @@ export * from "./gql.js";`);
}\`) as unknown as TypedDocumentString<VideoQuery, VideoQueryVariables>;
`);
});

it('correctly skips the typename addition for the root node for subscriptions', async () => {
const result = await executeCodegen({
schema: [
/* GraphQL */ `
schema {
query: Query
mutation: Mutation
subscription: Subscription
}
type Region {
regionId: Int!
regionDescription: String!
}
type Subscription {
onRegionCreated: Region!
}
type Query {
regions: [Region]
}
type Mutation {
createRegion(regionDescription: String!): Region
}
`,
],
documents: path.join(__dirname, 'fixtures/subscription-root-node.ts'),
generates: {
'out1/': {
preset,
config: {
documentMode: 'string',
},
documentTransforms: [addTypenameSelectionDocumentTransform],
},
},
});

const graphqlFile = result.find(file => file.filename === 'out1/graphql.ts');
expect(graphqlFile.content).toBeSimilarStringTo(`
/* eslint-disable */
import { DocumentTypeDecoration } from '@graphql-typed-document-node/core';
export type Maybe<T> = T | null;
export type InputMaybe<T> = Maybe<T>;
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
export type MakeEmpty<T extends { [key: string]: unknown }, K extends keyof T> = { [_ in K]?: never };
export type Incremental<T> = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never };
/** All built-in and custom scalars, mapped to their actual values */
export type Scalars = {
ID: { input: string; output: string; }
String: { input: string; output: string; }
Boolean: { input: boolean; output: boolean; }
Int: { input: number; output: number; }
Float: { input: number; output: number; }
};
export type Mutation = {
__typename?: 'Mutation';
createRegion?: Maybe<Region>;
};
export type MutationCreateRegionArgs = {
regionDescription: Scalars['String']['input'];
};
export type Query = {
__typename?: 'Query';
regions?: Maybe<Array<Maybe<Region>>>;
};
export type Region = {
__typename?: 'Region';
regionDescription: Scalars['String']['output'];
regionId: Scalars['Int']['output'];
};
export type Subscription = {
__typename?: 'Subscription';
onRegionCreated: Region;
};
export type OnRegionCreatedSubscriptionVariables = Exact<{ [key: string]: never; }>;
export type OnRegionCreatedSubscription = { __typename?: 'Subscription', onRegionCreated: { __typename: 'Region', regionId: number, regionDescription: string } };
export class TypedDocumentString<TResult, TVariables>
extends String
implements DocumentTypeDecoration<TResult, TVariables>
{
__apiType?: DocumentTypeDecoration<TResult, TVariables>['__apiType'];
constructor(private value: string, public __meta__?: Record<string, any>) {
super(value);
}
toString(): string & DocumentTypeDecoration<TResult, TVariables> {
return this.value;
}
}
export const OnRegionCreatedDocument = new TypedDocumentString(\`
subscription onRegionCreated {
onRegionCreated {
__typename
regionId
regionDescription
}
}
\`) as unknown as TypedDocumentString<OnRegionCreatedSubscription, OnRegionCreatedSubscriptionVariables>;
`);
});
});
});
12 changes: 12 additions & 0 deletions packages/presets/client/tests/fixtures/subscription-root-node.ts
@@ -0,0 +1,12 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
//@ts-ignore
import gql from 'gql-tag';

gql(`
subscription onRegionCreated {
onRegionCreated{
regionId
regionDescription
}
}
`);

0 comments on commit cd60e14

Please sign in to comment.