From ac75301ff8772cb4e6ee3a38fa87afadf8afbe21 Mon Sep 17 00:00:00 2001 From: Eddy Nguyen Date: Mon, 16 Dec 2024 19:35:53 +1100 Subject: [PATCH 1/4] [resolvers][federation] Fix mapper being incorrectly used as the base type for reference (#10216) --- .changeset/thick-pianos-smoke.md | 11 ++ .github/workflows/main.yml | 1 + dev-test/test-schema/resolvers-federation.ts | 18 +- .../src/base-resolvers-visitor.ts | 34 +++- .../plugins/typescript/resolvers/src/index.ts | 9 +- .../typescript/resolvers/src/visitor.ts | 16 +- .../__snapshots__/ts-resolvers.spec.ts.snap | 3 + .../ts-resolvers.federation.mappers.spec.ts | 168 ++++++++++++++++++ .../tests/ts-resolvers.federation.spec.ts | 143 ++++++--------- .../typescript/resolvers/tests/utils.ts | 15 ++ .../utils/plugins-helpers/src/federation.ts | 38 ++-- 11 files changed, 317 insertions(+), 139 deletions(-) create mode 100644 .changeset/thick-pianos-smoke.md create mode 100644 packages/plugins/typescript/resolvers/tests/ts-resolvers.federation.mappers.spec.ts create mode 100644 packages/plugins/typescript/resolvers/tests/utils.ts diff --git a/.changeset/thick-pianos-smoke.md b/.changeset/thick-pianos-smoke.md new file mode 100644 index 00000000000..569664420a9 --- /dev/null +++ b/.changeset/thick-pianos-smoke.md @@ -0,0 +1,11 @@ +--- +'@graphql-codegen/visitor-plugin-common': major +'@graphql-codegen/typescript-resolvers': major +'@graphql-codegen/plugin-helpers': major +--- + +Fix `mappers` usage with Federation + +`mappers` was previously used as `__resolveReference`'s first param (usually called "reference"). However, this is incorrect because `reference` interface comes directly from `@key` and `@requires` directives. This patch fixes the issue by creating a new `FederationTypes` type and use it as the base for federation entity types when being used to type entity references. + +BREAKING CHANGES: No longer generate `UnwrappedObject` utility type, as this was used to support the wrong previously generated type. diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index ff2632247d2..5ce63e4885b 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -7,6 +7,7 @@ on: pull_request: branches: - master + - federation-fixes # FIXME: Remove this line after the PR is merged env: NODE_OPTIONS: '--max_old_space_size=4096' diff --git a/dev-test/test-schema/resolvers-federation.ts b/dev-test/test-schema/resolvers-federation.ts index d2c05ac9d13..ba1feb00f47 100644 --- a/dev-test/test-schema/resolvers-federation.ts +++ b/dev-test/test-schema/resolvers-federation.ts @@ -128,6 +128,11 @@ export type DirectiveResolverFn TResult | Promise; +/** Mapping of federation types */ +export type FederationTypes = { + User: User; +}; + /** Mapping between all available schema types and the resolvers types */ export type ResolversTypes = { Address: ResolverTypeWrapper
; @@ -190,13 +195,14 @@ export type QueryResolvers< export type UserResolvers< ContextType = any, - ParentType extends ResolversParentTypes['User'] = ResolversParentTypes['User'] + ParentType extends ResolversParentTypes['User'] = ResolversParentTypes['User'], + FederationType extends FederationTypes['User'] = FederationTypes['User'] > = { __resolveReference?: ReferenceResolver< Maybe, { __typename: 'User' } & ( - | GraphQLRecursivePick - | GraphQLRecursivePick + | GraphQLRecursivePick + | GraphQLRecursivePick ), ContextType >; @@ -204,10 +210,10 @@ export type UserResolvers< email?: Resolver< ResolversTypes['String'], { __typename: 'User' } & ( - | GraphQLRecursivePick - | GraphQLRecursivePick + | GraphQLRecursivePick + | GraphQLRecursivePick ) & - GraphQLRecursivePick, + GraphQLRecursivePick, ContextType >; diff --git a/packages/plugins/other/visitor-plugin-common/src/base-resolvers-visitor.ts b/packages/plugins/other/visitor-plugin-common/src/base-resolvers-visitor.ts index 6adb71df597..9145c4b302a 100644 --- a/packages/plugins/other/visitor-plugin-common/src/base-resolvers-visitor.ts +++ b/packages/plugins/other/visitor-plugin-common/src/base-resolvers-visitor.ts @@ -1223,6 +1223,28 @@ export class BaseResolversVisitor< ).string; } + public buildFederationTypes(): string { + const federationMeta = this._federation.getMeta(); + + if (Object.keys(federationMeta).length === 0) { + return ''; + } + + const declarationKind = 'type'; + return new DeclarationBlock(this._declarationBlockConfig) + .export() + .asKind(declarationKind) + .withName(this.convertName('FederationTypes')) + .withComment('Mapping of federation types') + .withBlock( + Object.keys(federationMeta) + .map(typeName => { + return indent(`${typeName}: ${this.convertName(typeName)}${this.getPunctuation(declarationKind)}`); + }) + .join('\n') + ).string; + } + public get schema(): GraphQLSchema { return this._schema; } @@ -1498,6 +1520,7 @@ export class BaseResolversVisitor< fieldNode: original, parentType, parentTypeSignature: this.getParentTypeForSignature(node), + federationTypeSignature: 'FederationType', }); const mappedTypeKey = isSubscriptionType ? `${mappedType}, "${node.name}"` : mappedType; @@ -1618,10 +1641,19 @@ export class BaseResolversVisitor< ); } + const genericTypes: string[] = [ + `ContextType = ${this.config.contextType.type}`, + this.transformParentGenericType(parentType), + ]; + if (this._federation.getMeta()[typeName]) { + const typeRef = `${this.convertName('FederationTypes')}['${typeName}']`; + genericTypes.push(`FederationType extends ${typeRef} = ${typeRef}`); + } + const block = new DeclarationBlock(this._declarationBlockConfig) .export() .asKind(declarationKind) - .withName(name, ``) + .withName(name, `<${genericTypes.join(', ')}>`) .withBlock(fieldsContent.join('\n')); this._collectedResolvers[node.name as any] = { diff --git a/packages/plugins/typescript/resolvers/src/index.ts b/packages/plugins/typescript/resolvers/src/index.ts index e0d3ff293a3..692622a72bb 100644 --- a/packages/plugins/typescript/resolvers/src/index.ts +++ b/packages/plugins/typescript/resolvers/src/index.ts @@ -106,13 +106,6 @@ export type ResolverWithResolve = { const stitchingResolverUsage = `StitchingResolver`; if (visitor.hasFederation()) { - if (visitor.config.wrapFieldDefinitions) { - defsToInclude.push(`export type UnwrappedObject = { - [P in keyof T]: T[P] extends infer R | Promise | (() => infer R2 | Promise) - ? R & R2 : T[P] - };`); - } - defsToInclude.push( `export type ReferenceResolver = ( reference: TReference, @@ -244,6 +237,7 @@ export type DirectiveResolverFn TResult | Promise; `; + const federationTypes = visitor.buildFederationTypes(); const resolversTypeMapping = visitor.buildResolversTypes(); const resolversParentTypeMapping = visitor.buildResolversParentTypes(); const resolversUnionTypesMapping = visitor.buildResolversUnionTypes(); @@ -287,6 +281,7 @@ export type DirectiveResolverFn`; } - protected getParentTypeForSignature(node: FieldDefinitionNode) { - if (this._federation.isResolveReferenceField(node) && this.config.wrapFieldDefinitions) { - return 'UnwrappedObject'; - } - return 'ParentType'; - } - NamedType(node: NamedTypeNode): string { return `Maybe<${super.NamedType(node)}>`; } diff --git a/packages/plugins/typescript/resolvers/tests/__snapshots__/ts-resolvers.spec.ts.snap b/packages/plugins/typescript/resolvers/tests/__snapshots__/ts-resolvers.spec.ts.snap index 8783aac2cb1..0e3228d4016 100644 --- a/packages/plugins/typescript/resolvers/tests/__snapshots__/ts-resolvers.spec.ts.snap +++ b/packages/plugins/typescript/resolvers/tests/__snapshots__/ts-resolvers.spec.ts.snap @@ -166,6 +166,7 @@ export type DirectiveResolverFn TResult | Promise; + /** Mapping of union types */ export type ResolversUnionTypes<_RefType extends Record> = ResolversObject<{ ChildUnion: ( Omit & { parent?: Maybe<_RefType['MyType']> } ) | ( MyOtherType ); @@ -425,6 +426,7 @@ export type DirectiveResolverFn TResult | Promise; + /** Mapping of union types */ export type ResolversUnionTypes<_RefType extends Record> = ResolversObject<{ ChildUnion: ( Omit & { parent?: Types.Maybe<_RefType['MyType']> } ) | ( Types.MyOtherType ); @@ -770,6 +772,7 @@ export type DirectiveResolverFn TResult | Promise; + /** Mapping of union types */ export type ResolversUnionTypes<_RefType extends Record> = ResolversObject<{ ChildUnion: ( Omit & { parent?: Maybe<_RefType['MyType']> } ) | ( MyOtherType ); diff --git a/packages/plugins/typescript/resolvers/tests/ts-resolvers.federation.mappers.spec.ts b/packages/plugins/typescript/resolvers/tests/ts-resolvers.federation.mappers.spec.ts new file mode 100644 index 00000000000..9db647c00cc --- /dev/null +++ b/packages/plugins/typescript/resolvers/tests/ts-resolvers.federation.mappers.spec.ts @@ -0,0 +1,168 @@ +import '@graphql-codegen/testing'; +import { generate } from './utils'; + +describe('TypeScript Resolvers Plugin + Apollo Federation - mappers', () => { + it('generates FederationTypes and use it for reference type', async () => { + const federatedSchema = /* GraphQL */ ` + type Query { + me: User + } + + type User @key(fields: "id") { + id: ID! + name: String + } + + type UserProfile { + id: ID! + user: User! + } + `; + + const content = await generate({ + schema: federatedSchema, + config: { + federation: true, + mappers: { + User: './mappers#UserMapper', + }, + }, + }); + + // User should have it + expect(content).toMatchInlineSnapshot(` + "import { GraphQLResolveInfo } from 'graphql'; + import { UserMapper } from './mappers'; + export type Omit = Pick>; + + + export type ResolverTypeWrapper = Promise | T; + + export type ReferenceResolver = ( + reference: TReference, + context: TContext, + info: GraphQLResolveInfo + ) => Promise | TResult; + + type ScalarCheck = S extends true ? T : NullableCheck; + type NullableCheck = Maybe extends T ? Maybe, S>> : ListCheck; + type ListCheck = T extends (infer U)[] ? NullableCheck[] : GraphQLRecursivePick; + export type GraphQLRecursivePick = { [K in keyof T & keyof S]: ScalarCheck }; + + + export type ResolverWithResolve = { + resolve: ResolverFn; + }; + export type Resolver = ResolverFn | ResolverWithResolve; + + export type ResolverFn = ( + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo + ) => Promise | TResult; + + export type SubscriptionSubscribeFn = ( + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo + ) => AsyncIterable | Promise>; + + export type SubscriptionResolveFn = ( + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo + ) => TResult | Promise; + + export interface SubscriptionSubscriberObject { + subscribe: SubscriptionSubscribeFn<{ [key in TKey]: TResult }, TParent, TContext, TArgs>; + resolve?: SubscriptionResolveFn; + } + + export interface SubscriptionResolverObject { + subscribe: SubscriptionSubscribeFn; + resolve: SubscriptionResolveFn; + } + + export type SubscriptionObject = + | SubscriptionSubscriberObject + | SubscriptionResolverObject; + + export type SubscriptionResolver = + | ((...args: any[]) => SubscriptionObject) + | SubscriptionObject; + + export type TypeResolveFn = ( + parent: TParent, + context: TContext, + info: GraphQLResolveInfo + ) => Maybe | Promise>; + + export type IsTypeOfResolverFn = (obj: T, context: TContext, info: GraphQLResolveInfo) => boolean | Promise; + + export type NextResolverFn = () => Promise; + + export type DirectiveResolverFn = ( + next: NextResolverFn, + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo + ) => TResult | Promise; + + /** Mapping of federation types */ + export type FederationTypes = { + User: User; + }; + + + + /** Mapping between all available schema types and the resolvers types */ + export type ResolversTypes = { + Query: ResolverTypeWrapper<{}>; + User: ResolverTypeWrapper; + ID: ResolverTypeWrapper; + String: ResolverTypeWrapper; + UserProfile: ResolverTypeWrapper & { user: ResolversTypes['User'] }>; + Boolean: ResolverTypeWrapper; + }; + + /** Mapping between all available schema types and the resolvers parents */ + export type ResolversParentTypes = { + Query: {}; + User: UserMapper; + ID: Scalars['ID']['output']; + String: Scalars['String']['output']; + UserProfile: Omit & { user: ResolversParentTypes['User'] }; + Boolean: Scalars['Boolean']['output']; + }; + + export type QueryResolvers = { + me?: Resolver, ParentType, ContextType>; + }; + + export type UserResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; + id?: Resolver; + name?: Resolver, ParentType, ContextType>; + __isTypeOf?: IsTypeOfResolverFn; + }; + + export type UserProfileResolvers = { + id?: Resolver; + user?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; + }; + + export type Resolvers = { + Query?: QueryResolvers; + User?: UserResolvers; + UserProfile?: UserProfileResolvers; + }; + + " + `); + }); +}); diff --git a/packages/plugins/typescript/resolvers/tests/ts-resolvers.federation.spec.ts b/packages/plugins/typescript/resolvers/tests/ts-resolvers.federation.spec.ts index 1cae62b4c31..02a3d3ba0d7 100644 --- a/packages/plugins/typescript/resolvers/tests/ts-resolvers.federation.spec.ts +++ b/packages/plugins/typescript/resolvers/tests/ts-resolvers.federation.spec.ts @@ -85,8 +85,8 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { }); expect(content).toBeSimilarStringTo(` - export type UserResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; + export type UserResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; id?: Resolver; name?: Resolver, ParentType, ContextType>; username?: Resolver, ParentType, ContextType>; @@ -95,24 +95,24 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { `); expect(content).toBeSimilarStringTo(` - export type SingleResolvableResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'SingleResolvable' } & GraphQLRecursivePick, ContextType>; + export type SingleResolvableResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'SingleResolvable' } & GraphQLRecursivePick, ContextType>; id?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; `); expect(content).toBeSimilarStringTo(` - export type SingleNonResolvableResolvers = { - __resolveReference?: ReferenceResolver, ParentType, ContextType>; + export type SingleNonResolvableResolvers = { + __resolveReference?: ReferenceResolver, FederationType, ContextType>; id?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; `); expect(content).toBeSimilarStringTo(` - export type AtLeastOneResolvableResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'AtLeastOneResolvable' } & GraphQLRecursivePick, ContextType>; + export type AtLeastOneResolvableResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'AtLeastOneResolvable' } & GraphQLRecursivePick, ContextType>; id?: Resolver; id2?: Resolver; id3?: Resolver; @@ -121,8 +121,8 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { `); expect(content).toBeSimilarStringTo(` - export type MixedResolvableResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'MixedResolvable' } & (GraphQLRecursivePick | GraphQLRecursivePick), ContextType>; + export type MixedResolvableResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'MixedResolvable' } & (GraphQLRecursivePick | GraphQLRecursivePick), ContextType>; id?: Resolver; id2?: Resolver; id3?: Resolver; @@ -131,8 +131,8 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { `); expect(content).toBeSimilarStringTo(` - export type MultipleNonResolvableResolvers = { - __resolveReference?: ReferenceResolver, ParentType, ContextType>; + export type MultipleNonResolvableResolvers = { + __resolveReference?: ReferenceResolver, FederationType, ContextType>; id?: Resolver; id2?: Resolver; id3?: Resolver; @@ -211,8 +211,8 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { // User should have __resolveReference because it has resolvable @key (by default) expect(content).toBeSimilarStringTo(` - export type UserResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; + export type UserResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; id?: Resolver; name?: Resolver, ParentType, ContextType>; username?: Resolver, ParentType, ContextType>; @@ -222,8 +222,8 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { // SingleResolvable has __resolveReference because it has resolvable: true expect(content).toBeSimilarStringTo(` - export type SingleResolvableResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'SingleResolvable' } & GraphQLRecursivePick, ContextType>; + export type SingleResolvableResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'SingleResolvable' } & GraphQLRecursivePick, ContextType>; id?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; @@ -239,8 +239,8 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { // AtLeastOneResolvable has __resolveReference because it at least one resolvable expect(content).toBeSimilarStringTo(` - export type AtLeastOneResolvableResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'AtLeastOneResolvable' } & GraphQLRecursivePick, ContextType>; + export type AtLeastOneResolvableResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'AtLeastOneResolvable' } & GraphQLRecursivePick, ContextType>; id?: Resolver; id2?: Resolver; id3?: Resolver; @@ -250,8 +250,8 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { // MixedResolvable has __resolveReference and references for resolvable keys expect(content).toBeSimilarStringTo(` - export type MixedResolvableResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'MixedResolvable' } & (GraphQLRecursivePick | GraphQLRecursivePick), ContextType>; + export type MixedResolvableResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'MixedResolvable' } & (GraphQLRecursivePick | GraphQLRecursivePick), ContextType>; id?: Resolver; id2?: Resolver; id3?: Resolver; @@ -305,11 +305,11 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { // User should have it expect(content).toBeSimilarStringTo(` - __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; + __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; `); // Foo shouldn't because it doesn't have @key expect(content).not.toBeSimilarStringTo(` - __resolveReference?: ReferenceResolver, { __typename: 'Book' } & GraphQLRecursivePick, ContextType>; + __resolveReference?: ReferenceResolver, { __typename: 'Book' } & GraphQLRecursivePick, ContextType>; `); }); @@ -345,19 +345,19 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { }); expect(content).toBeSimilarStringTo(` - export type UserResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; - id?: Resolver, ContextType>; - name?: Resolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; + export type UserResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; + id?: Resolver, ContextType>; + name?: Resolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; __isTypeOf?: IsTypeOfResolverFn; } `); expect(content).toBeSimilarStringTo(` - export type NameResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'Name' } & GraphQLRecursivePick, ContextType>; - first?: Resolver, ContextType>; - last?: Resolver, ContextType>; + export type NameResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'Name' } & GraphQLRecursivePick, ContextType>; + first?: Resolver, ContextType>; + last?: Resolver, ContextType>; __isTypeOf?: IsTypeOfResolverFn; } `); @@ -386,10 +386,10 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { // User should have it expect(content).toBeSimilarStringTo(` - export type UserResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; - id?: Resolver, ContextType>; - username?: Resolver, { __typename: 'User' } & GraphQLRecursivePick & GraphQLRecursivePick, ContextType>; + export type UserResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; + id?: Resolver, ContextType>; + username?: Resolver, { __typename: 'User' } & GraphQLRecursivePick & GraphQLRecursivePick, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; `); @@ -423,9 +423,9 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { }); expect(content).toBeSimilarStringTo(` - export type UserResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; - username?: Resolver, { __typename: 'User' } & GraphQLRecursivePick & GraphQLRecursivePick, ContextType>; + export type UserResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; + username?: Resolver, { __typename: 'User' } & GraphQLRecursivePick & GraphQLRecursivePick, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; `); @@ -456,9 +456,9 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { }); expect(content).toBeSimilarStringTo(` - export type UserResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; - username?: Resolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; + export type UserResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; + username?: Resolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; `); @@ -489,8 +489,8 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { }); expect(content).toBeSimilarStringTo(` - export type UserResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; + export type UserResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; name?: Resolver; username?: Resolver, ParentType, ContextType>; __isTypeOf?: IsTypeOfResolverFn; @@ -568,10 +568,10 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { // UserResolver should not have a resolver function of name field expect(content).toBeSimilarStringTo(` - export type UserResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; - id?: Resolver, ContextType>; - name?: Resolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; + export type UserResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; + id?: Resolver, ContextType>; + name?: Resolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; `); @@ -695,10 +695,10 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { // User should have it expect(content).toBeSimilarStringTo(` - export type UserResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'User' } & (GraphQLRecursivePick | GraphQLRecursivePick), ContextType>; - name?: Resolver, { __typename: 'User' } & (GraphQLRecursivePick | GraphQLRecursivePick), ContextType>; - username?: Resolver, { __typename: 'User' } & (GraphQLRecursivePick | GraphQLRecursivePick), ContextType>; + export type UserResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'User' } & (GraphQLRecursivePick | GraphQLRecursivePick), ContextType>; + name?: Resolver, { __typename: 'User' } & (GraphQLRecursivePick | GraphQLRecursivePick), ContextType>; + username?: Resolver, { __typename: 'User' } & (GraphQLRecursivePick | GraphQLRecursivePick), ContextType>; __isTypeOf?: IsTypeOfResolverFn; }; `); @@ -763,49 +763,6 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { expect(content).not.toContain('GraphQLScalarType'); }); - describe('When field definition wrapping is enabled', () => { - it('should add the UnwrappedObject type', async () => { - const federatedSchema = /* GraphQL */ ` - type User @key(fields: "id") { - id: ID! - } - `; - - const content = await generate({ - schema: federatedSchema, - config: { - federation: true, - wrapFieldDefinitions: true, - }, - }); - - expect(content).toBeSimilarStringTo(`type UnwrappedObject = {`); - }); - - it('should add UnwrappedObject around ParentType for __resloveReference', async () => { - const federatedSchema = /* GraphQL */ ` - type User @key(fields: "id") { - id: ID! - } - `; - - const content = await generate({ - schema: federatedSchema, - config: { - federation: true, - wrapFieldDefinitions: true, - }, - }); - - // __resolveReference should be unwrapped - expect(content).toBeSimilarStringTo(` - __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, {"id":true}>, ContextType>; - `); - // but ID should not - expect(content).toBeSimilarStringTo(`id?: Resolver`); - }); - }); - describe('meta - generates federation meta correctly', () => { const federatedSchema = /* GraphQL */ ` scalar _FieldSet diff --git a/packages/plugins/typescript/resolvers/tests/utils.ts b/packages/plugins/typescript/resolvers/tests/utils.ts new file mode 100644 index 00000000000..20f77f1ac05 --- /dev/null +++ b/packages/plugins/typescript/resolvers/tests/utils.ts @@ -0,0 +1,15 @@ +import { codegen } from '@graphql-codegen/core'; +import { parse } from 'graphql'; +import { TypeScriptResolversPluginConfig } from '../src/config.js'; +import { plugin } from '../src/index.js'; + +export function generate({ schema, config }: { schema: string; config: TypeScriptResolversPluginConfig }) { + return codegen({ + filename: 'graphql.ts', + schema: parse(schema), + documents: [], + plugins: [{ 'typescript-resolvers': {} }], + config, + pluginMap: { 'typescript-resolvers': { plugin } }, + }); +} diff --git a/packages/utils/plugins-helpers/src/federation.ts b/packages/utils/plugins-helpers/src/federation.ts index d77277629e7..4da2e33e1c1 100644 --- a/packages/utils/plugins-helpers/src/federation.ts +++ b/packages/utils/plugins-helpers/src/federation.ts @@ -155,10 +155,12 @@ export class ApolloFederation { fieldNode, parentType, parentTypeSignature, + federationTypeSignature, }: { fieldNode: FieldDefinitionNode; parentType: GraphQLNamedType; parentTypeSignature: string; + federationTypeSignature: string; }) { if ( this.enabled && @@ -172,30 +174,32 @@ export class ApolloFederation { const { resolvableKeyDirectives } = objectTypeFederationDetails; - if (resolvableKeyDirectives.length) { - const outputs: string[] = [`{ __typename: '${parentType.name}' } &`]; + if (resolvableKeyDirectives.length === 0) { + return federationTypeSignature; + } - // Look for @requires and see what the service needs and gets - const requires = getDirectivesByName('requires', fieldNode).map(this.extractFieldSet); - const requiredFields = this.translateFieldSet(merge({}, ...requires), parentTypeSignature); + const outputs: string[] = [`{ __typename: '${parentType.name}' } &`]; - // @key() @key() - "primary keys" in Federation - const primaryKeys = resolvableKeyDirectives.map(def => { - const fields = this.extractFieldSet(def); - return this.translateFieldSet(fields, parentTypeSignature); - }); + // Look for @requires and see what the service needs and gets + const requires = getDirectivesByName('requires', fieldNode).map(this.extractFieldSet); + const requiredFields = this.translateFieldSet(merge({}, ...requires), federationTypeSignature); - const [open, close] = primaryKeys.length > 1 ? ['(', ')'] : ['', '']; + // @key() @key() - "primary keys" in Federation + const primaryKeys = resolvableKeyDirectives.map(def => { + const fields = this.extractFieldSet(def); + return this.translateFieldSet(fields, federationTypeSignature); + }); - outputs.push([open, primaryKeys.join(' | '), close].join('')); + const [open, close] = primaryKeys.length > 1 ? ['(', ')'] : ['', '']; - // include required fields - if (requires.length) { - outputs.push(`& ${requiredFields}`); - } + outputs.push([open, primaryKeys.join(' | '), close].join('')); - return outputs.join(' '); + // include required fields + if (requires.length) { + outputs.push(`& ${requiredFields}`); } + + return outputs.join(' '); } return parentTypeSignature; From a35011794414680e0938d02fc07a9bb8dbb985ef Mon Sep 17 00:00:00 2001 From: Eddy Nguyen Date: Wed, 29 Jan 2025 20:24:27 +1100 Subject: [PATCH 2/4] [resolvers][federation] Add `__resolveReference` to applicable `Interface` entities, fix Interface types having non-meta resolver fields (#10221) * Add __resolveReference for applicable Interfaces - Deprecate generateInternalResolversIfNeeded.__resolveReference - Fix tests - Deprecate onlyResolveTypeForInterfaces - Add changeset - Cleanup - Handle __resolveReference generation in Interface - Let FieldDefinition decide whether to generate __resolveReference by checking whether parent has resolvable key * Fix test --- .changeset/loud-suits-admire.md | 10 + .../src/base-resolvers-visitor.ts | 129 +++--- .../other/visitor-plugin-common/src/types.ts | 4 +- .../plugins/typescript/resolvers/src/index.ts | 10 +- .../typescript/resolvers/src/visitor.ts | 8 +- .../__snapshots__/ts-resolvers.spec.ts.snap | 18 - ...ts-resolvers.config.avoidOptionals.spec.ts | 1 - .../ts-resolvers.federation.interface.spec.ts | 198 +++++++++ .../tests/ts-resolvers.federation.spec.ts | 377 ++++-------------- .../tests/ts-resolvers.interface.spec.ts | 1 - .../tests/ts-resolvers.mapping.spec.ts | 5 - .../resolvers/tests/ts-resolvers.spec.ts | 21 - .../utils/plugins-helpers/src/federation.ts | 189 ++++++--- 13 files changed, 504 insertions(+), 467 deletions(-) create mode 100644 .changeset/loud-suits-admire.md create mode 100644 packages/plugins/typescript/resolvers/tests/ts-resolvers.federation.interface.spec.ts diff --git a/.changeset/loud-suits-admire.md b/.changeset/loud-suits-admire.md new file mode 100644 index 00000000000..3ecc3cbef0e --- /dev/null +++ b/.changeset/loud-suits-admire.md @@ -0,0 +1,10 @@ +--- +'@graphql-codegen/visitor-plugin-common': major +'@graphql-codegen/typescript-resolvers': major +'@graphql-codegen/plugin-helpers': major +--- + +Ensure Federation Interfaces have `__resolveReference` if they are resolvable entities + +BREAKING CHANGES: Deprecate `onlyResolveTypeForInterfaces` because majority of use cases cannot implement resolvers in Interfaces. +BREAKING CHANGES: Deprecate `generateInternalResolversIfNeeded.__resolveReference` because types do not have `__resolveReference` if they are not Federation entities or are not resolvable. Users should not have to manually set this option. This option was put in to wait for this major version. diff --git a/packages/plugins/other/visitor-plugin-common/src/base-resolvers-visitor.ts b/packages/plugins/other/visitor-plugin-common/src/base-resolvers-visitor.ts index 9145c4b302a..bcdb478d8b0 100644 --- a/packages/plugins/other/visitor-plugin-common/src/base-resolvers-visitor.ts +++ b/packages/plugins/other/visitor-plugin-common/src/base-resolvers-visitor.ts @@ -1,4 +1,4 @@ -import { ApolloFederation, checkObjectTypeFederationDetails, getBaseType } from '@graphql-codegen/plugin-helpers'; +import { ApolloFederation, type FederationMeta, getBaseType } from '@graphql-codegen/plugin-helpers'; import { getRootTypeNames } from '@graphql-tools/utils'; import autoBind from 'auto-bind'; import { @@ -78,13 +78,15 @@ export interface ParsedResolversConfig extends ParsedConfig { allResolversTypeName: string; internalResolversPrefix: string; generateInternalResolversIfNeeded: NormalizedGenerateInternalResolversIfNeededConfig; - onlyResolveTypeForInterfaces: boolean; directiveResolverMappings: Record; resolversNonOptionalTypename: ResolversNonOptionalTypenameConfig; avoidCheckingAbstractTypesRecursively: boolean; } -type FieldDefinitionPrintFn = (parentName: string, avoidResolverOptionals: boolean) => string | null; +type FieldDefinitionPrintFn = ( + parentName: string, + avoidResolverOptionals: boolean +) => { value: string | null; meta: { federation?: { isResolveReference: boolean } } }; export interface RootResolver { content: string; generatedResolverTypes: { @@ -584,20 +586,13 @@ export interface RawResolversConfig extends RawConfig { internalResolversPrefix?: string; /** * @type object - * @default { __resolveReference: false } + * @default {} * @description If relevant internal resolvers are set to `true`, the resolver type will only be generated if the right conditions are met. * Enabling this allows a more correct type generation for the resolvers. * For example: * - `__isTypeOf` is generated for implementing types and union members - * - `__resolveReference` is generated for federation types that have at least one resolvable `@key` directive */ generateInternalResolversIfNeeded?: GenerateInternalResolversIfNeededConfig; - /** - * @type boolean - * @default false - * @description Turning this flag to `true` will generate resolver signature that has only `resolveType` for interfaces, forcing developers to write inherited type resolvers in the type itself. - */ - onlyResolveTypeForInterfaces?: boolean; /** * @description Makes `__typename` of resolver mappings non-optional without affecting the base types. * @default false @@ -700,7 +695,8 @@ export class BaseResolversVisitor< rawConfig: TRawConfig, additionalConfig: TPluginConfig, private _schema: GraphQLSchema, - defaultScalars: NormalizedScalarsMap = DEFAULT_SCALARS + defaultScalars: NormalizedScalarsMap = DEFAULT_SCALARS, + federationMeta: FederationMeta = {} ) { super(rawConfig, { immutableTypes: getConfigValue(rawConfig.immutableTypes, false), @@ -714,7 +710,6 @@ export class BaseResolversVisitor< mapOrStr: rawConfig.enumValues, }), addUnderscoreToArgsType: getConfigValue(rawConfig.addUnderscoreToArgsType, false), - onlyResolveTypeForInterfaces: getConfigValue(rawConfig.onlyResolveTypeForInterfaces, false), contextType: parseMapper(rawConfig.contextType || 'any', 'ContextType'), fieldContextTypes: getConfigValue(rawConfig.fieldContextTypes, []), directiveContextTypes: getConfigValue(rawConfig.directiveContextTypes, []), @@ -729,9 +724,7 @@ export class BaseResolversVisitor< mappers: transformMappers(rawConfig.mappers || {}, rawConfig.mapperTypeSuffix), scalars: buildScalarsFromConfig(_schema, rawConfig, defaultScalars), internalResolversPrefix: getConfigValue(rawConfig.internalResolversPrefix, '__'), - generateInternalResolversIfNeeded: { - __resolveReference: rawConfig.generateInternalResolversIfNeeded?.__resolveReference ?? false, - }, + generateInternalResolversIfNeeded: {}, resolversNonOptionalTypename: normalizeResolversNonOptionalTypename( getConfigValue(rawConfig.resolversNonOptionalTypename, false) ), @@ -740,7 +733,11 @@ export class BaseResolversVisitor< } as TPluginConfig); autoBind(this); - this._federation = new ApolloFederation({ enabled: this.config.federation, schema: this.schema }); + this._federation = new ApolloFederation({ + enabled: this.config.federation, + schema: this.schema, + meta: federationMeta, + }); this._rootTypeNames = getRootTypeNames(_schema); this._variablesTransformer = new OperationVariablesToObject( this.scalars, @@ -1358,7 +1355,9 @@ export class BaseResolversVisitor< const federationMeta = this._federation.getMeta()[schemaTypeName]; if (federationMeta) { - userDefinedTypes[schemaTypeName].federation = federationMeta; + userDefinedTypes[schemaTypeName].federation = { + hasResolveReference: federationMeta.hasResolveReference, + }; } } @@ -1474,9 +1473,10 @@ export class BaseResolversVisitor< const baseType = getBaseTypeNode(original.type); const realType = baseType.name.value; const parentType = this.schema.getType(parentName); + const meta: ReturnType['meta'] = {}; if (this._federation.skipField({ fieldNode: original, parentType })) { - return null; + return { value: null, meta }; } const contextType = this.getContextType(parentName, node); @@ -1516,7 +1516,7 @@ export class BaseResolversVisitor< } } - const parentTypeSignature = this._federation.transformParentType({ + const parentTypeSignature = this._federation.transformFieldParentType({ fieldNode: original, parentType, parentTypeSignature: this.getParentTypeForSignature(node), @@ -1545,29 +1545,22 @@ export class BaseResolversVisitor< }; if (this._federation.isResolveReferenceField(node)) { - if (this.config.generateInternalResolversIfNeeded.__resolveReference) { - const federationDetails = checkObjectTypeFederationDetails( - parentType.astNode as ObjectTypeDefinitionNode, - this._schema - ); - - if (!federationDetails || federationDetails.resolvableKeyDirectives.length === 0) { - return ''; - } + if (!this._federation.getMeta()[parentType.name].hasResolveReference) { + return { value: '', meta }; } - - this._federation.setMeta(parentType.name, { hasResolveReference: true }); signature.type = 'ReferenceResolver'; - if (signature.genericTypes.length >= 3) { - signature.genericTypes = signature.genericTypes.slice(0, 3); - } + signature.genericTypes = [mappedTypeKey, parentTypeSignature, contextType]; + meta.federation = { isResolveReference: true }; } - return indent( - `${signature.name}${signature.modifier}: ${signature.type}<${signature.genericTypes.join( - ', ' - )}>${this.getPunctuation(declarationKind)}` - ); + return { + value: indent( + `${signature.name}${signature.modifier}: ${signature.type}<${signature.genericTypes.join( + ', ' + )}>${this.getPunctuation(declarationKind)}` + ), + meta, + }; }; } @@ -1628,7 +1621,7 @@ export class BaseResolversVisitor< (rootType === 'mutation' && this.config.avoidOptionals.mutation) || (rootType === 'subscription' && this.config.avoidOptionals.subscription) || (rootType === false && this.config.avoidOptionals.resolvers) - ); + ).value; }); if (!rootType) { @@ -1645,10 +1638,11 @@ export class BaseResolversVisitor< `ContextType = ${this.config.contextType.type}`, this.transformParentGenericType(parentType), ]; - if (this._federation.getMeta()[typeName]) { - const typeRef = `${this.convertName('FederationTypes')}['${typeName}']`; - genericTypes.push(`FederationType extends ${typeRef} = ${typeRef}`); - } + this._federation.addFederationTypeGenericIfApplicable({ + genericTypes, + federationTypesType: this.convertName('FederationTypes'), + typeName, + }); const block = new DeclarationBlock(this._declarationBlockConfig) .export() @@ -1837,25 +1831,44 @@ export class BaseResolversVisitor< } const parentType = this.getParentTypeToUse(typeName); + + const genericTypes: string[] = [ + `ContextType = ${this.config.contextType.type}`, + this.transformParentGenericType(parentType), + ]; + this._federation.addFederationTypeGenericIfApplicable({ + genericTypes, + federationTypesType: this.convertName('FederationTypes'), + typeName, + }); + const possibleTypes = implementingTypes.map(name => `'${name}'`).join(' | ') || 'null'; - const fields = this.config.onlyResolveTypeForInterfaces ? [] : node.fields || []; + + // An Interface has __resolveType resolver, and no other fields. + const blockFields: string[] = [ + indent( + `${this.config.internalResolversPrefix}resolveType${ + this.config.optionalResolveType ? '?' : '' + }: TypeResolveFn<${possibleTypes}, ParentType, ContextType>${this.getPunctuation(declarationKind)}` + ), + ]; + + // An Interface in Federation may have the additional __resolveReference resolver, if resolvable. + // So, we filter out the normal fields declared on the Interface and add the __resolveReference resolver. + const fields = (node.fields as unknown as FieldDefinitionPrintFn[]).map(f => + f(typeName, this.config.avoidOptionals.resolvers) + ); + for (const field of fields) { + if (field.meta.federation?.isResolveReference) { + blockFields.push(field.value); + } + } return new DeclarationBlock(this._declarationBlockConfig) .export() .asKind(declarationKind) - .withName(name, ``) - .withBlock( - [ - indent( - `${this.config.internalResolversPrefix}resolveType${ - this.config.optionalResolveType ? '?' : '' - }: TypeResolveFn<${possibleTypes}, ParentType, ContextType>${this.getPunctuation(declarationKind)}` - ), - ...(fields as unknown as FieldDefinitionPrintFn[]).map(f => - f(typeName, this.config.avoidOptionals.resolvers) - ), - ].join('\n') - ).string; + .withName(name, `<${genericTypes.join(', ')}>`) + .withBlock(blockFields.join('\n')).string; } SchemaDefinition() { diff --git a/packages/plugins/other/visitor-plugin-common/src/types.ts b/packages/plugins/other/visitor-plugin-common/src/types.ts index 16f64e0f029..e2e2004bfea 100644 --- a/packages/plugins/other/visitor-plugin-common/src/types.ts +++ b/packages/plugins/other/visitor-plugin-common/src/types.ts @@ -139,7 +139,5 @@ export interface CustomDirectivesConfig { apolloUnmask?: boolean; } -export interface GenerateInternalResolversIfNeededConfig { - __resolveReference?: boolean; -} +export interface GenerateInternalResolversIfNeededConfig {} export type NormalizedGenerateInternalResolversIfNeededConfig = Required; diff --git a/packages/plugins/typescript/resolvers/src/index.ts b/packages/plugins/typescript/resolvers/src/index.ts index 692622a72bb..3518dbb0004 100644 --- a/packages/plugins/typescript/resolvers/src/index.ts +++ b/packages/plugins/typescript/resolvers/src/index.ts @@ -75,8 +75,14 @@ export type Resolver${capitalizedDirectiveName}WithResolve { - constructor(pluginConfig: TypeScriptResolversPluginConfig, schema: GraphQLSchema) { + constructor(pluginConfig: TypeScriptResolversPluginConfig, schema: GraphQLSchema, federationMeta: FederationMeta) { super( pluginConfig, { @@ -34,7 +36,9 @@ export class TypeScriptResolversVisitor extends BaseResolversVisitor< allowParentTypeOverride: getConfigValue(pluginConfig.allowParentTypeOverride, false), optionalInfoArgument: getConfigValue(pluginConfig.optionalInfoArgument, false), } as ParsedTypeScriptResolversConfig, - schema + schema, + DEFAULT_SCALARS, + federationMeta ); autoBind(this); this.setVariablesTransformer( diff --git a/packages/plugins/typescript/resolvers/tests/__snapshots__/ts-resolvers.spec.ts.snap b/packages/plugins/typescript/resolvers/tests/__snapshots__/ts-resolvers.spec.ts.snap index 0e3228d4016..eeda2eb6b4d 100644 --- a/packages/plugins/typescript/resolvers/tests/__snapshots__/ts-resolvers.spec.ts.snap +++ b/packages/plugins/typescript/resolvers/tests/__snapshots__/ts-resolvers.spec.ts.snap @@ -272,7 +272,6 @@ export type SubscriptionResolvers = ResolversObject<{ __resolveType: TypeResolveFn<'SomeNode', ParentType, ContextType>; - id?: Resolver; }>; export type SomeNodeResolvers = ResolversObject<{ @@ -282,19 +281,14 @@ export type SomeNodeResolvers = ResolversObject<{ __resolveType: TypeResolveFn<'AnotherNodeWithChild' | 'AnotherNodeWithAll', ParentType, ContextType>; - id?: Resolver; }>; export type WithChildResolvers = ResolversObject<{ __resolveType: TypeResolveFn<'AnotherNodeWithChild' | 'AnotherNodeWithAll', ParentType, ContextType>; - unionChild?: Resolver, ParentType, ContextType>; - node?: Resolver, ParentType, ContextType>; }>; export type WithChildrenResolvers = ResolversObject<{ __resolveType: TypeResolveFn<'AnotherNodeWithAll', ParentType, ContextType>; - unionChildren?: Resolver, ParentType, ContextType>; - nodes?: Resolver, ParentType, ContextType>; }>; export type AnotherNodeWithChildResolvers = ResolversObject<{ @@ -532,7 +526,6 @@ export type SubscriptionResolvers = ResolversObject<{ __resolveType: TypeResolveFn<'SomeNode', ParentType, ContextType>; - id?: Resolver; }>; export type SomeNodeResolvers = ResolversObject<{ @@ -542,19 +535,14 @@ export type SomeNodeResolvers = ResolversObject<{ __resolveType: TypeResolveFn<'AnotherNodeWithChild' | 'AnotherNodeWithAll', ParentType, ContextType>; - id?: Resolver; }>; export type WithChildResolvers = ResolversObject<{ __resolveType: TypeResolveFn<'AnotherNodeWithChild' | 'AnotherNodeWithAll', ParentType, ContextType>; - unionChild?: Resolver, ParentType, ContextType>; - node?: Resolver, ParentType, ContextType>; }>; export type WithChildrenResolvers = ResolversObject<{ __resolveType: TypeResolveFn<'AnotherNodeWithAll', ParentType, ContextType>; - unionChildren?: Resolver, ParentType, ContextType>; - nodes?: Resolver, ParentType, ContextType>; }>; export type AnotherNodeWithChildResolvers = ResolversObject<{ @@ -878,7 +866,6 @@ export type SubscriptionResolvers = ResolversObject<{ __resolveType: TypeResolveFn<'SomeNode', ParentType, ContextType>; - id?: Resolver; }>; export type SomeNodeResolvers = ResolversObject<{ @@ -888,19 +875,14 @@ export type SomeNodeResolvers = ResolversObject<{ __resolveType: TypeResolveFn<'AnotherNodeWithChild' | 'AnotherNodeWithAll', ParentType, ContextType>; - id?: Resolver; }>; export type WithChildResolvers = ResolversObject<{ __resolveType: TypeResolveFn<'AnotherNodeWithChild' | 'AnotherNodeWithAll', ParentType, ContextType>; - unionChild?: Resolver, ParentType, ContextType>; - node?: Resolver, ParentType, ContextType>; }>; export type WithChildrenResolvers = ResolversObject<{ __resolveType: TypeResolveFn<'AnotherNodeWithAll', ParentType, ContextType>; - unionChildren?: Resolver, ParentType, ContextType>; - nodes?: Resolver, ParentType, ContextType>; }>; export type AnotherNodeWithChildResolvers = ResolversObject<{ diff --git a/packages/plugins/typescript/resolvers/tests/ts-resolvers.config.avoidOptionals.spec.ts b/packages/plugins/typescript/resolvers/tests/ts-resolvers.config.avoidOptionals.spec.ts index ff7b7730a36..42918d7b710 100644 --- a/packages/plugins/typescript/resolvers/tests/ts-resolvers.config.avoidOptionals.spec.ts +++ b/packages/plugins/typescript/resolvers/tests/ts-resolvers.config.avoidOptionals.spec.ts @@ -53,7 +53,6 @@ describe('TypeScript Resolvers Plugin - config.avoidOptionals', () => { expect(result.content).toBeSimilarStringTo(` export type NodeResolvers = { __resolveType: TypeResolveFn<'SomeNode', ParentType, ContextType>; - id: Resolver; }; `); diff --git a/packages/plugins/typescript/resolvers/tests/ts-resolvers.federation.interface.spec.ts b/packages/plugins/typescript/resolvers/tests/ts-resolvers.federation.interface.spec.ts new file mode 100644 index 00000000000..8ef9616ff40 --- /dev/null +++ b/packages/plugins/typescript/resolvers/tests/ts-resolvers.federation.interface.spec.ts @@ -0,0 +1,198 @@ +import '@graphql-codegen/testing'; +import { generate } from './utils'; + +describe('TypeScript Resolvers Plugin + Apollo Federation - Interface', () => { + it('generates __resolveReference for Interfaces with @key', async () => { + const federatedSchema = /* GraphQL */ ` + type Query { + me: Person + } + + interface Person @key(fields: "id") { + id: ID! + name: PersonName! + } + + type User implements Person @key(fields: "id") { + id: ID! + name: PersonName! + } + + type Admin implements Person @key(fields: "id") { + id: ID! + name: PersonName! + canImpersonate: Boolean! + } + + type PersonName { + first: String! + last: String! + } + `; + + const content = await generate({ + schema: federatedSchema, + config: { + federation: true, + }, + }); + + expect(content).toMatchInlineSnapshot(` + "import { GraphQLResolveInfo } from 'graphql'; + + + export type ResolverTypeWrapper = Promise | T; + + export type ReferenceResolver = ( + reference: TReference, + context: TContext, + info: GraphQLResolveInfo + ) => Promise | TResult; + + type ScalarCheck = S extends true ? T : NullableCheck; + type NullableCheck = Maybe extends T ? Maybe, S>> : ListCheck; + type ListCheck = T extends (infer U)[] ? NullableCheck[] : GraphQLRecursivePick; + export type GraphQLRecursivePick = { [K in keyof T & keyof S]: ScalarCheck }; + + + export type ResolverWithResolve = { + resolve: ResolverFn; + }; + export type Resolver = ResolverFn | ResolverWithResolve; + + export type ResolverFn = ( + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo + ) => Promise | TResult; + + export type SubscriptionSubscribeFn = ( + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo + ) => AsyncIterable | Promise>; + + export type SubscriptionResolveFn = ( + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo + ) => TResult | Promise; + + export interface SubscriptionSubscriberObject { + subscribe: SubscriptionSubscribeFn<{ [key in TKey]: TResult }, TParent, TContext, TArgs>; + resolve?: SubscriptionResolveFn; + } + + export interface SubscriptionResolverObject { + subscribe: SubscriptionSubscribeFn; + resolve: SubscriptionResolveFn; + } + + export type SubscriptionObject = + | SubscriptionSubscriberObject + | SubscriptionResolverObject; + + export type SubscriptionResolver = + | ((...args: any[]) => SubscriptionObject) + | SubscriptionObject; + + export type TypeResolveFn = ( + parent: TParent, + context: TContext, + info: GraphQLResolveInfo + ) => Maybe | Promise>; + + export type IsTypeOfResolverFn = (obj: T, context: TContext, info: GraphQLResolveInfo) => boolean | Promise; + + export type NextResolverFn = () => Promise; + + export type DirectiveResolverFn = ( + next: NextResolverFn, + parent: TParent, + args: TArgs, + context: TContext, + info: GraphQLResolveInfo + ) => TResult | Promise; + + /** Mapping of federation types */ + export type FederationTypes = { + Person: Person; + User: User; + Admin: Admin; + }; + + + /** Mapping of interface types */ + export type ResolversInterfaceTypes<_RefType extends Record> = { + Person: ( User ) | ( Admin ); + }; + + /** Mapping between all available schema types and the resolvers types */ + export type ResolversTypes = { + Query: ResolverTypeWrapper<{}>; + Person: ResolverTypeWrapper['Person']>; + ID: ResolverTypeWrapper; + User: ResolverTypeWrapper; + Admin: ResolverTypeWrapper; + Boolean: ResolverTypeWrapper; + PersonName: ResolverTypeWrapper; + String: ResolverTypeWrapper; + }; + + /** Mapping between all available schema types and the resolvers parents */ + export type ResolversParentTypes = { + Query: {}; + Person: ResolversInterfaceTypes['Person']; + ID: Scalars['ID']['output']; + User: User; + Admin: Admin; + Boolean: Scalars['Boolean']['output']; + PersonName: PersonName; + String: Scalars['String']['output']; + }; + + export type QueryResolvers = { + me?: Resolver, ParentType, ContextType>; + }; + + export type PersonResolvers = { + __resolveType: TypeResolveFn<'User' | 'Admin', ParentType, ContextType>; + __resolveReference?: ReferenceResolver, { __typename: 'Person' } & GraphQLRecursivePick, ContextType>; + }; + + export type UserResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; + id?: Resolver; + name?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; + }; + + export type AdminResolvers = { + __resolveReference?: ReferenceResolver, { __typename: 'Admin' } & GraphQLRecursivePick, ContextType>; + id?: Resolver; + name?: Resolver; + canImpersonate?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; + }; + + export type PersonNameResolvers = { + first?: Resolver; + last?: Resolver; + __isTypeOf?: IsTypeOfResolverFn; + }; + + export type Resolvers = { + Query?: QueryResolvers; + Person?: PersonResolvers; + User?: UserResolvers; + Admin?: AdminResolvers; + PersonName?: PersonNameResolvers; + }; + + " + `); + }); +}); diff --git a/packages/plugins/typescript/resolvers/tests/ts-resolvers.federation.spec.ts b/packages/plugins/typescript/resolvers/tests/ts-resolvers.federation.spec.ts index 02a3d3ba0d7..71723b55187 100644 --- a/packages/plugins/typescript/resolvers/tests/ts-resolvers.federation.spec.ts +++ b/packages/plugins/typescript/resolvers/tests/ts-resolvers.federation.spec.ts @@ -24,7 +24,7 @@ function generate({ schema, config }: { schema: string; config: TypeScriptResolv } describe('TypeScript Resolvers Plugin + Apollo Federation', () => { - describe('adds __resolveReference', () => { + it('generates __resolveReference for object types with resolvable @key', async () => { const federatedSchema = /* GraphQL */ ` type Query { allUsers: [User] @@ -76,141 +76,15 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { } `; - it('when generateInternalResolversIfNeeded.__resolveReference = false, generates optional __resolveReference for object types with @key', async () => { - const content = await generate({ - schema: federatedSchema, - config: { - federation: true, - }, - }); - - expect(content).toBeSimilarStringTo(` - export type UserResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; - id?: Resolver; - name?: Resolver, ParentType, ContextType>; - username?: Resolver, ParentType, ContextType>; - __isTypeOf?: IsTypeOfResolverFn; - }; - `); - - expect(content).toBeSimilarStringTo(` - export type SingleResolvableResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'SingleResolvable' } & GraphQLRecursivePick, ContextType>; - id?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; - }; - `); - - expect(content).toBeSimilarStringTo(` - export type SingleNonResolvableResolvers = { - __resolveReference?: ReferenceResolver, FederationType, ContextType>; - id?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; - }; - `); - - expect(content).toBeSimilarStringTo(` - export type AtLeastOneResolvableResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'AtLeastOneResolvable' } & GraphQLRecursivePick, ContextType>; - id?: Resolver; - id2?: Resolver; - id3?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; - }; - `); - - expect(content).toBeSimilarStringTo(` - export type MixedResolvableResolvers = { - __resolveReference?: ReferenceResolver, { __typename: 'MixedResolvable' } & (GraphQLRecursivePick | GraphQLRecursivePick), ContextType>; - id?: Resolver; - id2?: Resolver; - id3?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; - }; - `); - - expect(content).toBeSimilarStringTo(` - export type MultipleNonResolvableResolvers = { - __resolveReference?: ReferenceResolver, FederationType, ContextType>; - id?: Resolver; - id2?: Resolver; - id3?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; - }; - `); - - // Book does NOT have __resolveReference because it doesn't have @key - expect(content).toBeSimilarStringTo(` - export type BookResolvers = { - id?: Resolver; - __isTypeOf?: IsTypeOfResolverFn; - }; - `); + const content = await generate({ + schema: federatedSchema, + config: { + federation: true, + }, }); - it('when generateInternalResolversIfNeeded.__resolveReference = true, generates required __resolveReference for object types with resolvable @key', async () => { - const federatedSchema = /* GraphQL */ ` - type Query { - allUsers: [User] - } - - type User @key(fields: "id") { - id: ID! - name: String - username: String - } - - type Book { - id: ID! - } - - type SingleResolvable @key(fields: "id", resolvable: true) { - id: ID! - } - - type SingleNonResolvable @key(fields: "id", resolvable: false) { - id: ID! - } - - type AtLeastOneResolvable - @key(fields: "id", resolvable: false) - @key(fields: "id2", resolvable: true) - @key(fields: "id3", resolvable: false) { - id: ID! - id2: ID! - id3: ID! - } - - type MixedResolvable - @key(fields: "id") - @key(fields: "id2", resolvable: true) - @key(fields: "id3", resolvable: false) { - id: ID! - id2: ID! - id3: ID! - } - - type MultipleNonResolvable - @key(fields: "id", resolvable: false) - @key(fields: "id2", resolvable: false) - @key(fields: "id3", resolvable: false) { - id: ID! - id2: ID! - id3: ID! - } - `; - - const content = await generate({ - schema: federatedSchema, - config: { - federation: true, - generateInternalResolversIfNeeded: { __resolveReference: true }, - }, - }); - - // User should have __resolveReference because it has resolvable @key (by default) - expect(content).toBeSimilarStringTo(` + // User should have __resolveReference because it has resolvable @key (by default) + expect(content).toBeSimilarStringTo(` export type UserResolvers = { __resolveReference?: ReferenceResolver, { __typename: 'User' } & GraphQLRecursivePick, ContextType>; id?: Resolver; @@ -220,8 +94,8 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { }; `); - // SingleResolvable has __resolveReference because it has resolvable: true - expect(content).toBeSimilarStringTo(` + // SingleResolvable has __resolveReference because it has resolvable: true + expect(content).toBeSimilarStringTo(` export type SingleResolvableResolvers = { __resolveReference?: ReferenceResolver, { __typename: 'SingleResolvable' } & GraphQLRecursivePick, ContextType>; id?: Resolver; @@ -229,16 +103,16 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { }; `); - // SingleNonResolvable does NOT have __resolveReference because it has resolvable: false - expect(content).toBeSimilarStringTo(` + // SingleNonResolvable does NOT have __resolveReference because it has resolvable: false + expect(content).toBeSimilarStringTo(` export type SingleNonResolvableResolvers = { id?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; `); - // AtLeastOneResolvable has __resolveReference because it at least one resolvable - expect(content).toBeSimilarStringTo(` + // AtLeastOneResolvable has __resolveReference because it at least one resolvable + expect(content).toBeSimilarStringTo(` export type AtLeastOneResolvableResolvers = { __resolveReference?: ReferenceResolver, { __typename: 'AtLeastOneResolvable' } & GraphQLRecursivePick, ContextType>; id?: Resolver; @@ -248,8 +122,8 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { }; `); - // MixedResolvable has __resolveReference and references for resolvable keys - expect(content).toBeSimilarStringTo(` + // MixedResolvable has __resolveReference and references for resolvable keys + expect(content).toBeSimilarStringTo(` export type MixedResolvableResolvers = { __resolveReference?: ReferenceResolver, { __typename: 'MixedResolvable' } & (GraphQLRecursivePick | GraphQLRecursivePick), ContextType>; id?: Resolver; @@ -259,8 +133,8 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { }; `); - // MultipleNonResolvableResolvers does NOT have __resolveReference because all keys are non-resolvable - expect(content).toBeSimilarStringTo(` + // MultipleNonResolvableResolvers does NOT have __resolveReference because all keys are non-resolvable + expect(content).toBeSimilarStringTo(` export type MultipleNonResolvableResolvers = { id?: Resolver; id2?: Resolver; @@ -269,14 +143,13 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { }; `); - // Book does NOT have __resolveReference because it doesn't have @key - expect(content).toBeSimilarStringTo(` + // Book does NOT have __resolveReference because it doesn't have @key + expect(content).toBeSimilarStringTo(` export type BookResolvers = { id?: Resolver; __isTypeOf?: IsTypeOfResolverFn; }; `); - }); }); it('should support extend keyword', async () => { @@ -498,50 +371,6 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { `); }); - it.skip('should handle interface types', async () => { - const federatedSchema = /* GraphQL */ ` - type Query { - people: [Person] - } - - extend interface Person @key(fields: "name { first last }") { - name: Name! @external - age: Int @requires(fields: "name") - } - - extend type User implements Person @key(fields: "name { first last }") { - name: Name! @external - age: Int @requires(fields: "name { first last }") - username: String - } - - type Admin implements Person @key(fields: "name { first last }") { - name: Name! @external - age: Int @requires(fields: "name { first last }") - permissions: [String!]! - } - - extend type Name { - first: String! @external - last: String! @external - } - `; - - const content = await generate({ - schema: federatedSchema, - config: { - federation: true, - }, - }); - - expect(content).toBeSimilarStringTo(` - export type PersonResolvers = { - __resolveType: TypeResolveFn<'User' | 'Admin', ParentType, ContextType>; - age?: Resolver, { __typename: 'User' | 'Admin' } & GraphQLRecursivePick, ContextType>; - }; - `); - }); - it('should skip to generate resolvers of fields with @external directive', async () => { const federatedSchema = /* GraphQL */ ` type Query { @@ -763,132 +592,68 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => { expect(content).not.toContain('GraphQLScalarType'); }); - describe('meta - generates federation meta correctly', () => { - const federatedSchema = /* GraphQL */ ` - scalar _FieldSet - directive @key(fields: _FieldSet!, resolvable: Boolean) repeatable on OBJECT | INTERFACE - - type Query { - user: UserPayload! - allUsers: [User] - } - - type User @key(fields: "id") { - id: ID! - name: String - username: String - } + describe('meta', () => { + it('generates federation meta correctly', async () => { + const federatedSchema = /* GraphQL */ ` + scalar _FieldSet + directive @key(fields: _FieldSet!, resolvable: Boolean) repeatable on OBJECT | INTERFACE - interface Node { - id: ID! - } + type Query { + user: UserPayload! + allUsers: [User] + } - type UserOk { - id: ID! - } - type UserError { - message: String! - } - union UserPayload = UserOk | UserError + type User @key(fields: "id") { + id: ID! + name: String + username: String + } - enum Country { - FR - US - } + interface Node { + id: ID! + } - type NotResolvable @key(fields: "id", resolvable: false) { - id: ID! - } + type UserOk { + id: ID! + } + type UserError { + message: String! + } + union UserPayload = UserOk | UserError - type Resolvable @key(fields: "id", resolvable: true) { - id: ID! - } + enum Country { + FR + US + } - type MultipleResolvable - @key(fields: "id") - @key(fields: "id2", resolvable: true) - @key(fields: "id3", resolvable: false) { - id: ID! - id2: ID! - id3: ID! - } + type NotResolvable @key(fields: "id", resolvable: false) { + id: ID! + } - type MultipleNonResolvable - @key(fields: "id", resolvable: false) - @key(fields: "id2", resolvable: false) - @key(fields: "id3", resolvable: false) { - id: ID! - id2: ID! - id3: ID! - } - `; + type Resolvable @key(fields: "id", resolvable: true) { + id: ID! + } - it('when generateInternalResolversIfNeeded.__resolveReference = false', async () => { - const result = await plugin(buildSchema(federatedSchema), [], { federation: true }, { outputFile: '' }); + type MultipleResolvable + @key(fields: "id") + @key(fields: "id2", resolvable: true) + @key(fields: "id3", resolvable: false) { + id: ID! + id2: ID! + id3: ID! + } - expect(result.meta?.generatedResolverTypes).toMatchInlineSnapshot(` - Object { - "resolversMap": Object { - "name": "Resolvers", - }, - "userDefined": Object { - "MultipleNonResolvable": Object { - "federation": Object { - "hasResolveReference": true, - }, - "name": "MultipleNonResolvableResolvers", - }, - "MultipleResolvable": Object { - "federation": Object { - "hasResolveReference": true, - }, - "name": "MultipleResolvableResolvers", - }, - "Node": Object { - "name": "NodeResolvers", - }, - "NotResolvable": Object { - "federation": Object { - "hasResolveReference": true, - }, - "name": "NotResolvableResolvers", - }, - "Query": Object { - "name": "QueryResolvers", - }, - "Resolvable": Object { - "federation": Object { - "hasResolveReference": true, - }, - "name": "ResolvableResolvers", - }, - "User": Object { - "federation": Object { - "hasResolveReference": true, - }, - "name": "UserResolvers", - }, - "UserError": Object { - "name": "UserErrorResolvers", - }, - "UserOk": Object { - "name": "UserOkResolvers", - }, - "UserPayload": Object { - "name": "UserPayloadResolvers", - }, - }, + type MultipleNonResolvable + @key(fields: "id", resolvable: false) + @key(fields: "id2", resolvable: false) + @key(fields: "id3", resolvable: false) { + id: ID! + id2: ID! + id3: ID! } - `); - }); + `; - it('when generateInternalResolversIfNeeded.__resolveReference = true', async () => { - const result = await plugin( - buildSchema(federatedSchema), - [], - { federation: true, generateInternalResolversIfNeeded: { __resolveReference: true } }, - { outputFile: '' } - ); + const result = await plugin(buildSchema(federatedSchema), [], { federation: true }, { outputFile: '' }); expect(result.meta?.generatedResolverTypes).toMatchInlineSnapshot(` Object { diff --git a/packages/plugins/typescript/resolvers/tests/ts-resolvers.interface.spec.ts b/packages/plugins/typescript/resolvers/tests/ts-resolvers.interface.spec.ts index 332216111f6..64e997ac25d 100644 --- a/packages/plugins/typescript/resolvers/tests/ts-resolvers.interface.spec.ts +++ b/packages/plugins/typescript/resolvers/tests/ts-resolvers.interface.spec.ts @@ -162,7 +162,6 @@ describe('TypeScript Resolvers Plugin - Interfaces', () => { expect(result.content).toBeSimilarStringTo(` export type NodeResolvers = { __resolveType: TypeResolveFn; - id?: Resolver; }; `); }); diff --git a/packages/plugins/typescript/resolvers/tests/ts-resolvers.mapping.spec.ts b/packages/plugins/typescript/resolvers/tests/ts-resolvers.mapping.spec.ts index c595ebe7041..17e67670276 100644 --- a/packages/plugins/typescript/resolvers/tests/ts-resolvers.mapping.spec.ts +++ b/packages/plugins/typescript/resolvers/tests/ts-resolvers.mapping.spec.ts @@ -1265,7 +1265,6 @@ describe('TypeScript Resolvers Plugin - Mapping', () => { expect(result.content).toBeSimilarStringTo(` export type NodeResolvers = { __resolveType: TypeResolveFn<'SomeNode', ParentType, ContextType>; - id?: Resolver; }; `); @@ -1348,7 +1347,6 @@ describe('TypeScript Resolvers Plugin - Mapping', () => { expect(result.content).toBeSimilarStringTo(` export type NodeResolvers = { __resolveType: TypeResolveFn<'SomeNode', ParentType, ContextType>; - id?: Resolver; }; `); @@ -1429,7 +1427,6 @@ describe('TypeScript Resolvers Plugin - Mapping', () => { expect(result.content).toBeSimilarStringTo(` export type NodeResolvers = { __resolveType: TypeResolveFn<'SomeNode', ParentType, ContextType>; - id?: Resolver; }; `); @@ -1499,7 +1496,6 @@ describe('TypeScript Resolvers Plugin - Mapping', () => { expect(result.content).toBeSimilarStringTo(` export type NodeResolvers = { __resolveType: TypeResolveFn<'SomeNode', ParentType, ContextType>; - id?: Resolver; }; `); @@ -1569,7 +1565,6 @@ describe('TypeScript Resolvers Plugin - Mapping', () => { expect(result.content).toBeSimilarStringTo(` export type NodeResolvers = { __resolveType: TypeResolveFn<'SomeNode', ParentType, ContextType>; - id?: Resolver; }; `); diff --git a/packages/plugins/typescript/resolvers/tests/ts-resolvers.spec.ts b/packages/plugins/typescript/resolvers/tests/ts-resolvers.spec.ts index a1d13fe829e..8028e1522af 100644 --- a/packages/plugins/typescript/resolvers/tests/ts-resolvers.spec.ts +++ b/packages/plugins/typescript/resolvers/tests/ts-resolvers.spec.ts @@ -85,19 +85,6 @@ describe('TypeScript Resolvers Plugin', () => { }); describe('Config', () => { - it('onlyResolveTypeForInterfaces - should allow to have only resolveType for interfaces', async () => { - const config = { - onlyResolveTypeForInterfaces: true, - }; - const result = await plugin(resolversTestingSchema, [], config, { outputFile: '' }); - const content = await resolversTestingValidate(result, config, resolversTestingSchema); - - expect(content).toBeSimilarStringTo(` - export type NodeResolvers = { - __resolveType: TypeResolveFn<'SomeNode', ParentType, ContextType>; - };`); - }); - it('optionalInfoArgument - should allow to have optional info argument', async () => { const config = { noSchemaStitching: true, @@ -655,7 +642,6 @@ __isTypeOf?: IsTypeOfResolverFn; expect(result.content).toBeSimilarStringTo(` export type NodeResolvers = { __resolveType?: TypeResolveFn<'SomeNode', ParentType, ContextType>; - id?: Resolver; }; `); }); @@ -705,7 +691,6 @@ __isTypeOf?: IsTypeOfResolverFn; expect(result.content).toBeSimilarStringTo(` export type NodeResolvers = { __resolveType: TypeResolveFn<'SomeNode', ParentType, ContextType>; - id?: Resolver; }; `); @@ -778,7 +763,6 @@ __isTypeOf?: IsTypeOfResolverFn; expect(result.content).toBeSimilarStringTo(` export type NodeResolvers = { __resolveType: TypeResolveFn<'SomeNode', ParentType, ContextType>; - id?: Resolver; }; `); @@ -869,7 +853,6 @@ __isTypeOf?: IsTypeOfResolverFn; expect(result.content).toBeSimilarStringTo(` export type NodeResolvers = { __resolveType: TypeResolveFn<'SomeNode', ParentType, ContextType>; - id?: Resolver; }; `); @@ -944,7 +927,6 @@ __isTypeOf?: IsTypeOfResolverFn; expect(result.content).toBeSimilarStringTo(` export type NodeResolvers = { __resolveType: TypeResolveFn<'SomeNode', ParentType, ContextType>; - id?: Resolver; }; `); @@ -1018,7 +1000,6 @@ __isTypeOf?: IsTypeOfResolverFn; expect(result.content).toBeSimilarStringTo(` export type NodeResolvers = { __resolveType: TypeResolveFn<'SomeNode', ParentType, ContextType>; - id?: Resolver; }; `); @@ -1093,7 +1074,6 @@ __isTypeOf?: IsTypeOfResolverFn; expect(result.content).toBeSimilarStringTo(` export type NodeResolvers = { __resolveType: TypeResolveFn<'SomeNode', ParentType, ContextType>; - id?: Resolver; }; `); @@ -2360,7 +2340,6 @@ export type ResolverFn = ( expect(result.content).toBeSimilarStringTo(` export type NodeResolvers = { resolveType: TypeResolveFn<'SomeNode', ParentType, ContextType>; - id?: Resolver; }; `); diff --git a/packages/utils/plugins-helpers/src/federation.ts b/packages/utils/plugins-helpers/src/federation.ts index 4da2e33e1c1..3ac608bf604 100644 --- a/packages/utils/plugins-helpers/src/federation.ts +++ b/packages/utils/plugins-helpers/src/federation.ts @@ -1,11 +1,13 @@ -import { astFromObjectType, getRootTypeNames, MapperKind, mapSchema } from '@graphql-tools/utils'; +import { astFromInterfaceType, astFromObjectType, getRootTypeNames, MapperKind, mapSchema } from '@graphql-tools/utils'; import { DefinitionNode, DirectiveNode, FieldDefinitionNode, + GraphQLInterfaceType, GraphQLNamedType, GraphQLObjectType, GraphQLSchema, + isInterfaceType, isObjectType, ObjectTypeDefinitionNode, OperationDefinitionNode, @@ -28,14 +30,67 @@ export const federationSpec = parse(/* GraphQL */ ` directive @key(fields: _FieldSet!) on OBJECT | INTERFACE `); +interface TypeMeta { + hasResolveReference: boolean; + resolvableKeyDirectives: readonly DirectiveNode[]; +} + +export type FederationMeta = { [typeName: string]: TypeMeta }; + /** - * Adds `__resolveReference` in each ObjectType involved in Federation. + * Adds `__resolveReference` in each ObjectType and InterfaceType involved in Federation. + * We do this to utilise the existing FieldDefinition logic of the plugin, which includes many logic: + * - mapper + * - return type * @param schema */ -export function addFederationReferencesToSchema(schema: GraphQLSchema): GraphQLSchema { - return mapSchema(schema, { +export function addFederationReferencesToSchema(schema: GraphQLSchema): { + transformedSchema: GraphQLSchema; + federationMeta: FederationMeta; +} { + const setFederationMeta = ({ + meta, + typeName, + update, + }: { + meta: FederationMeta; + typeName: string; + update: TypeMeta; + }): void => { + meta[typeName] = { ...(meta[typeName] || { hasResolveReference: false, resolvableKeyDirectives: [] }), ...update }; + }; + + const federationMeta: FederationMeta = {}; + + const transformedSchema = mapSchema(schema, { + [MapperKind.INTERFACE_TYPE]: type => { + const federationDetails = checkTypeFederationDetails(type, schema); + if (federationDetails && federationDetails.resolvableKeyDirectives.length > 0) { + const typeConfig = type.toConfig(); + typeConfig.fields = { + [resolveReferenceFieldName]: { + type, + }, + ...typeConfig.fields, + }; + + setFederationMeta({ + meta: federationMeta, + typeName: type.name, + update: { + hasResolveReference: true, + resolvableKeyDirectives: federationDetails.resolvableKeyDirectives, + }, + }); + + return new GraphQLInterfaceType(typeConfig); + } + + return type; + }, [MapperKind.OBJECT_TYPE]: type => { - if (checkObjectTypeFederationDetails(type, schema)) { + const federationDetails = checkTypeFederationDetails(type, schema); + if (federationDetails && federationDetails.resolvableKeyDirectives.length > 0) { const typeConfig = type.toConfig(); typeConfig.fields = { [resolveReferenceFieldName]: { @@ -44,11 +99,25 @@ export function addFederationReferencesToSchema(schema: GraphQLSchema): GraphQLS ...typeConfig.fields, }; + setFederationMeta({ + meta: federationMeta, + typeName: type.name, + update: { + hasResolveReference: true, + resolvableKeyDirectives: federationDetails.resolvableKeyDirectives, + }, + }); + return new GraphQLObjectType(typeConfig); } return type; }, }); + + return { + transformedSchema, + federationMeta, + }; } /** @@ -82,20 +151,17 @@ export function removeFederation(schema: GraphQLSchema): GraphQLSchema { const resolveReferenceFieldName = '__resolveReference'; -interface TypeMeta { - hasResolveReference: boolean; -} - export class ApolloFederation { private enabled = false; private schema: GraphQLSchema; private providesMap: Record; - protected meta: { [typename: string]: TypeMeta } = {}; + protected meta: FederationMeta = {}; - constructor({ enabled, schema }: { enabled: boolean; schema: GraphQLSchema }) { + constructor({ enabled, schema, meta }: { enabled: boolean; schema: GraphQLSchema; meta: FederationMeta }) { this.enabled = enabled; this.schema = schema; this.providesMap = this.createMapOfProvides(); + this.meta = meta; } /** @@ -135,7 +201,11 @@ export class ApolloFederation { * @param data */ skipField({ fieldNode, parentType }: { fieldNode: FieldDefinitionNode; parentType: GraphQLNamedType }): boolean { - if (!this.enabled || !isObjectType(parentType) || !checkObjectTypeFederationDetails(parentType, this.schema)) { + if ( + !this.enabled || + !(isObjectType(parentType) && !isInterfaceType(parentType)) || + !checkTypeFederationDetails(parentType, this.schema) + ) { return false; } @@ -148,10 +218,10 @@ export class ApolloFederation { } /** - * Transforms ParentType signature in ObjectTypes involved in Federation + * Transforms a field's ParentType signature in ObjectTypes or InterfaceTypes involved in Federation * @param data */ - transformParentType({ + transformFieldParentType({ fieldNode, parentType, parentTypeSignature, @@ -162,52 +232,67 @@ export class ApolloFederation { parentTypeSignature: string; federationTypeSignature: string; }) { - if ( - this.enabled && - isObjectType(parentType) && - (isTypeExtension(parentType, this.schema) || fieldNode.name.value === resolveReferenceFieldName) - ) { - const objectTypeFederationDetails = checkObjectTypeFederationDetails(parentType, this.schema); - if (!objectTypeFederationDetails) { - return parentTypeSignature; - } + if (!this.enabled) { + return parentTypeSignature; + } - const { resolvableKeyDirectives } = objectTypeFederationDetails; + const parentTypeMeta = this.getMeta()[parentType.name]; + if (!parentTypeMeta?.hasResolveReference) { + return parentTypeSignature; + } - if (resolvableKeyDirectives.length === 0) { - return federationTypeSignature; - } + const isObjectFieldWithFederationRef = + isObjectType(parentType) && + (nodeHasTypeExtension(parentType, this.schema) || fieldNode.name.value === resolveReferenceFieldName); - const outputs: string[] = [`{ __typename: '${parentType.name}' } &`]; + const isInterfaceFieldWithFederationRef = + isInterfaceType(parentType) && fieldNode.name.value === resolveReferenceFieldName; - // Look for @requires and see what the service needs and gets - const requires = getDirectivesByName('requires', fieldNode).map(this.extractFieldSet); - const requiredFields = this.translateFieldSet(merge({}, ...requires), federationTypeSignature); + if (!isObjectFieldWithFederationRef && !isInterfaceFieldWithFederationRef) { + return parentTypeSignature; + } - // @key() @key() - "primary keys" in Federation - const primaryKeys = resolvableKeyDirectives.map(def => { - const fields = this.extractFieldSet(def); - return this.translateFieldSet(fields, federationTypeSignature); - }); + const outputs: string[] = [`{ __typename: '${parentType.name}' } &`]; - const [open, close] = primaryKeys.length > 1 ? ['(', ')'] : ['', '']; + // Look for @requires and see what the service needs and gets + const requires = getDirectivesByName('requires', fieldNode).map(this.extractFieldSet); + const requiredFields = this.translateFieldSet(merge({}, ...requires), federationTypeSignature); - outputs.push([open, primaryKeys.join(' | '), close].join('')); + // @key() @key() - "primary keys" in Federation + const primaryKeys = parentTypeMeta.resolvableKeyDirectives.map(def => { + const fields = this.extractFieldSet(def); + return this.translateFieldSet(fields, federationTypeSignature); + }); - // include required fields - if (requires.length) { - outputs.push(`& ${requiredFields}`); - } + const [open, close] = primaryKeys.length > 1 ? ['(', ')'] : ['', '']; - return outputs.join(' '); + outputs.push([open, primaryKeys.join(' | '), close].join('')); + + // include required fields + if (requires.length) { + outputs.push(`& ${requiredFields}`); } - return parentTypeSignature; + return outputs.join(' '); } - setMeta(typename: string, update: Partial): void { - this.meta[typename] = { ...(this.meta[typename] || { hasResolveReference: false }), ...update }; + addFederationTypeGenericIfApplicable({ + genericTypes, + typeName, + federationTypesType, + }: { + genericTypes: string[]; + typeName: string; + federationTypesType: string; + }): void { + if (!this.getMeta()[typeName]) { + return; + } + + const typeRef = `${federationTypesType}['${typeName}']`; + genericTypes.push(`FederationType extends ${typeRef} = ${typeRef}`); } + getMeta() { return this.meta; } @@ -295,14 +380,18 @@ export class ApolloFederation { * Checks if Object Type is involved in Federation. Based on `@key` directive * @param node Type */ -export function checkObjectTypeFederationDetails( - node: ObjectTypeDefinitionNode | GraphQLObjectType, +function checkTypeFederationDetails( + node: ObjectTypeDefinitionNode | GraphQLObjectType | GraphQLInterfaceType, schema: GraphQLSchema ): { resolvableKeyDirectives: readonly DirectiveNode[] } | false { const { name: { value: name }, directives, - } = isObjectType(node) ? astFromObjectType(node, schema) : node; + } = isObjectType(node) + ? astFromObjectType(node, schema) + : isInterfaceType(node) + ? astFromInterfaceType(node, schema) + : node; const rootTypeNames = getRootTypeNames(schema); const isNotRoot = !rootTypeNames.has(name); @@ -352,7 +441,7 @@ function getDirectivesByName( * Based on if any of its fields contain the `@external` directive * @param node Type */ -function isTypeExtension(node: ObjectTypeDefinitionNode | GraphQLObjectType, schema: GraphQLSchema): boolean { +function nodeHasTypeExtension(node: ObjectTypeDefinitionNode | GraphQLObjectType, schema: GraphQLSchema): boolean { const definition = isObjectType(node) ? node.astNode || astFromObjectType(node, schema) : node; return definition.fields?.some(field => getDirectivesByName('external', field).length); } From 56fea453e3e3cbd3d8441ee4c4a4f952d6a83f90 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 29 Jan 2025 09:24:59 +0000 Subject: [PATCH 3/4] chore(dependencies): updated changesets for modified dependencies --- .changeset/@graphql-codegen_cli-10218-dependencies.md | 6 ++++++ .../@graphql-codegen_client-preset-10218-dependencies.md | 9 +++++++++ ...phql-codegen_gql-tag-operations-10218-dependencies.md | 5 +++++ ...-codegen_graphql-modules-preset-10218-dependencies.md | 5 +++++ ...hql-codegen_typed-document-node-10218-dependencies.md | 5 +++++ .../@graphql-codegen_typescript-10218-dependencies.md | 5 +++++ ...degen_typescript-document-nodes-10218-dependencies.md | 5 +++++ ...l-codegen_typescript-operations-10218-dependencies.md | 6 ++++++ ...ql-codegen_typescript-resolvers-10218-dependencies.md | 6 ++++++ 9 files changed, 52 insertions(+) create mode 100644 .changeset/@graphql-codegen_cli-10218-dependencies.md create mode 100644 .changeset/@graphql-codegen_client-preset-10218-dependencies.md create mode 100644 .changeset/@graphql-codegen_gql-tag-operations-10218-dependencies.md create mode 100644 .changeset/@graphql-codegen_graphql-modules-preset-10218-dependencies.md create mode 100644 .changeset/@graphql-codegen_typed-document-node-10218-dependencies.md create mode 100644 .changeset/@graphql-codegen_typescript-10218-dependencies.md create mode 100644 .changeset/@graphql-codegen_typescript-document-nodes-10218-dependencies.md create mode 100644 .changeset/@graphql-codegen_typescript-operations-10218-dependencies.md create mode 100644 .changeset/@graphql-codegen_typescript-resolvers-10218-dependencies.md diff --git a/.changeset/@graphql-codegen_cli-10218-dependencies.md b/.changeset/@graphql-codegen_cli-10218-dependencies.md new file mode 100644 index 00000000000..51d8dedf635 --- /dev/null +++ b/.changeset/@graphql-codegen_cli-10218-dependencies.md @@ -0,0 +1,6 @@ +--- +"@graphql-codegen/cli": patch +--- +dependencies updates: + - Updated dependency [`@graphql-codegen/client-preset@^4.6.0` ↗︎](https://www.npmjs.com/package/@graphql-codegen/client-preset/v/4.6.0) (from `^4.4.0`, in `dependencies`) + - Updated dependency [`@whatwg-node/fetch@^0.10.0` ↗︎](https://www.npmjs.com/package/@whatwg-node/fetch/v/0.10.0) (from `^0.9.20`, in `dependencies`) diff --git a/.changeset/@graphql-codegen_client-preset-10218-dependencies.md b/.changeset/@graphql-codegen_client-preset-10218-dependencies.md new file mode 100644 index 00000000000..b1fe1cfaf73 --- /dev/null +++ b/.changeset/@graphql-codegen_client-preset-10218-dependencies.md @@ -0,0 +1,9 @@ +--- +"@graphql-codegen/client-preset": patch +--- +dependencies updates: + - Updated dependency [`@graphql-codegen/typed-document-node@^5.0.13` ↗︎](https://www.npmjs.com/package/@graphql-codegen/typed-document-node/v/5.0.13) (from `^5.0.12`, in `dependencies`) + - Updated dependency [`@graphql-codegen/typescript@^4.1.3` ↗︎](https://www.npmjs.com/package/@graphql-codegen/typescript/v/4.1.3) (from `^4.1.2`, in `dependencies`) + - Updated dependency [`@graphql-codegen/typescript-operations@^4.4.1` ↗︎](https://www.npmjs.com/package/@graphql-codegen/typescript-operations/v/4.4.1) (from `^4.4.0`, in `dependencies`) + - Updated dependency [`@graphql-codegen/gql-tag-operations@4.0.13` ↗︎](https://www.npmjs.com/package/@graphql-codegen/gql-tag-operations/v/4.0.13) (from `4.0.12`, in `dependencies`) + - Updated dependency [`@graphql-codegen/visitor-plugin-common@^5.6.1` ↗︎](https://www.npmjs.com/package/@graphql-codegen/visitor-plugin-common/v/5.6.1) (from `^5.6.0`, in `dependencies`) diff --git a/.changeset/@graphql-codegen_gql-tag-operations-10218-dependencies.md b/.changeset/@graphql-codegen_gql-tag-operations-10218-dependencies.md new file mode 100644 index 00000000000..2521d242fbe --- /dev/null +++ b/.changeset/@graphql-codegen_gql-tag-operations-10218-dependencies.md @@ -0,0 +1,5 @@ +--- +"@graphql-codegen/gql-tag-operations": patch +--- +dependencies updates: + - Updated dependency [`@graphql-codegen/visitor-plugin-common@5.6.1` ↗︎](https://www.npmjs.com/package/@graphql-codegen/visitor-plugin-common/v/5.6.1) (from `5.6.0`, in `dependencies`) diff --git a/.changeset/@graphql-codegen_graphql-modules-preset-10218-dependencies.md b/.changeset/@graphql-codegen_graphql-modules-preset-10218-dependencies.md new file mode 100644 index 00000000000..1012f14b647 --- /dev/null +++ b/.changeset/@graphql-codegen_graphql-modules-preset-10218-dependencies.md @@ -0,0 +1,5 @@ +--- +"@graphql-codegen/graphql-modules-preset": patch +--- +dependencies updates: + - Updated dependency [`@graphql-codegen/visitor-plugin-common@5.6.1` ↗︎](https://www.npmjs.com/package/@graphql-codegen/visitor-plugin-common/v/5.6.1) (from `5.6.0`, in `dependencies`) diff --git a/.changeset/@graphql-codegen_typed-document-node-10218-dependencies.md b/.changeset/@graphql-codegen_typed-document-node-10218-dependencies.md new file mode 100644 index 00000000000..968f5921f33 --- /dev/null +++ b/.changeset/@graphql-codegen_typed-document-node-10218-dependencies.md @@ -0,0 +1,5 @@ +--- +"@graphql-codegen/typed-document-node": patch +--- +dependencies updates: + - Updated dependency [`@graphql-codegen/visitor-plugin-common@5.6.1` ↗︎](https://www.npmjs.com/package/@graphql-codegen/visitor-plugin-common/v/5.6.1) (from `5.6.0`, in `dependencies`) diff --git a/.changeset/@graphql-codegen_typescript-10218-dependencies.md b/.changeset/@graphql-codegen_typescript-10218-dependencies.md new file mode 100644 index 00000000000..fb52b9eee9c --- /dev/null +++ b/.changeset/@graphql-codegen_typescript-10218-dependencies.md @@ -0,0 +1,5 @@ +--- +"@graphql-codegen/typescript": patch +--- +dependencies updates: + - Updated dependency [`@graphql-codegen/visitor-plugin-common@5.6.1` ↗︎](https://www.npmjs.com/package/@graphql-codegen/visitor-plugin-common/v/5.6.1) (from `5.6.0`, in `dependencies`) diff --git a/.changeset/@graphql-codegen_typescript-document-nodes-10218-dependencies.md b/.changeset/@graphql-codegen_typescript-document-nodes-10218-dependencies.md new file mode 100644 index 00000000000..51cb49e5535 --- /dev/null +++ b/.changeset/@graphql-codegen_typescript-document-nodes-10218-dependencies.md @@ -0,0 +1,5 @@ +--- +"@graphql-codegen/typescript-document-nodes": patch +--- +dependencies updates: + - Updated dependency [`@graphql-codegen/visitor-plugin-common@5.6.1` ↗︎](https://www.npmjs.com/package/@graphql-codegen/visitor-plugin-common/v/5.6.1) (from `5.6.0`, in `dependencies`) diff --git a/.changeset/@graphql-codegen_typescript-operations-10218-dependencies.md b/.changeset/@graphql-codegen_typescript-operations-10218-dependencies.md new file mode 100644 index 00000000000..619352f5678 --- /dev/null +++ b/.changeset/@graphql-codegen_typescript-operations-10218-dependencies.md @@ -0,0 +1,6 @@ +--- +"@graphql-codegen/typescript-operations": patch +--- +dependencies updates: + - Updated dependency [`@graphql-codegen/typescript@^4.1.3` ↗︎](https://www.npmjs.com/package/@graphql-codegen/typescript/v/4.1.3) (from `^4.1.2`, in `dependencies`) + - Updated dependency [`@graphql-codegen/visitor-plugin-common@5.6.1` ↗︎](https://www.npmjs.com/package/@graphql-codegen/visitor-plugin-common/v/5.6.1) (from `5.6.0`, in `dependencies`) diff --git a/.changeset/@graphql-codegen_typescript-resolvers-10218-dependencies.md b/.changeset/@graphql-codegen_typescript-resolvers-10218-dependencies.md new file mode 100644 index 00000000000..bda76bce275 --- /dev/null +++ b/.changeset/@graphql-codegen_typescript-resolvers-10218-dependencies.md @@ -0,0 +1,6 @@ +--- +"@graphql-codegen/typescript-resolvers": patch +--- +dependencies updates: + - Updated dependency [`@graphql-codegen/typescript@^4.1.3` ↗︎](https://www.npmjs.com/package/@graphql-codegen/typescript/v/4.1.3) (from `^4.1.2`, in `dependencies`) + - Updated dependency [`@graphql-codegen/visitor-plugin-common@5.6.1` ↗︎](https://www.npmjs.com/package/@graphql-codegen/visitor-plugin-common/v/5.6.1) (from `5.6.0`, in `dependencies`) From cc7a80af0c203d9cbee94aab2632fc51ba495c55 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Wed, 29 Jan 2025 09:27:57 +0000 Subject: [PATCH 4/4] chore(dependencies): updated changesets for modified dependencies --- .changeset/@graphql-codegen_cli-10218-dependencies.md | 6 ------ .../@graphql-codegen_client-preset-10218-dependencies.md | 9 --------- ...phql-codegen_gql-tag-operations-10218-dependencies.md | 5 ----- ...-codegen_graphql-modules-preset-10218-dependencies.md | 5 ----- ...hql-codegen_typed-document-node-10218-dependencies.md | 5 ----- .../@graphql-codegen_typescript-10218-dependencies.md | 5 ----- ...degen_typescript-document-nodes-10218-dependencies.md | 5 ----- ...l-codegen_typescript-operations-10218-dependencies.md | 6 ------ ...ql-codegen_typescript-resolvers-10218-dependencies.md | 6 ------ 9 files changed, 52 deletions(-) delete mode 100644 .changeset/@graphql-codegen_cli-10218-dependencies.md delete mode 100644 .changeset/@graphql-codegen_client-preset-10218-dependencies.md delete mode 100644 .changeset/@graphql-codegen_gql-tag-operations-10218-dependencies.md delete mode 100644 .changeset/@graphql-codegen_graphql-modules-preset-10218-dependencies.md delete mode 100644 .changeset/@graphql-codegen_typed-document-node-10218-dependencies.md delete mode 100644 .changeset/@graphql-codegen_typescript-10218-dependencies.md delete mode 100644 .changeset/@graphql-codegen_typescript-document-nodes-10218-dependencies.md delete mode 100644 .changeset/@graphql-codegen_typescript-operations-10218-dependencies.md delete mode 100644 .changeset/@graphql-codegen_typescript-resolvers-10218-dependencies.md diff --git a/.changeset/@graphql-codegen_cli-10218-dependencies.md b/.changeset/@graphql-codegen_cli-10218-dependencies.md deleted file mode 100644 index 51d8dedf635..00000000000 --- a/.changeset/@graphql-codegen_cli-10218-dependencies.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@graphql-codegen/cli": patch ---- -dependencies updates: - - Updated dependency [`@graphql-codegen/client-preset@^4.6.0` ↗︎](https://www.npmjs.com/package/@graphql-codegen/client-preset/v/4.6.0) (from `^4.4.0`, in `dependencies`) - - Updated dependency [`@whatwg-node/fetch@^0.10.0` ↗︎](https://www.npmjs.com/package/@whatwg-node/fetch/v/0.10.0) (from `^0.9.20`, in `dependencies`) diff --git a/.changeset/@graphql-codegen_client-preset-10218-dependencies.md b/.changeset/@graphql-codegen_client-preset-10218-dependencies.md deleted file mode 100644 index b1fe1cfaf73..00000000000 --- a/.changeset/@graphql-codegen_client-preset-10218-dependencies.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -"@graphql-codegen/client-preset": patch ---- -dependencies updates: - - Updated dependency [`@graphql-codegen/typed-document-node@^5.0.13` ↗︎](https://www.npmjs.com/package/@graphql-codegen/typed-document-node/v/5.0.13) (from `^5.0.12`, in `dependencies`) - - Updated dependency [`@graphql-codegen/typescript@^4.1.3` ↗︎](https://www.npmjs.com/package/@graphql-codegen/typescript/v/4.1.3) (from `^4.1.2`, in `dependencies`) - - Updated dependency [`@graphql-codegen/typescript-operations@^4.4.1` ↗︎](https://www.npmjs.com/package/@graphql-codegen/typescript-operations/v/4.4.1) (from `^4.4.0`, in `dependencies`) - - Updated dependency [`@graphql-codegen/gql-tag-operations@4.0.13` ↗︎](https://www.npmjs.com/package/@graphql-codegen/gql-tag-operations/v/4.0.13) (from `4.0.12`, in `dependencies`) - - Updated dependency [`@graphql-codegen/visitor-plugin-common@^5.6.1` ↗︎](https://www.npmjs.com/package/@graphql-codegen/visitor-plugin-common/v/5.6.1) (from `^5.6.0`, in `dependencies`) diff --git a/.changeset/@graphql-codegen_gql-tag-operations-10218-dependencies.md b/.changeset/@graphql-codegen_gql-tag-operations-10218-dependencies.md deleted file mode 100644 index 2521d242fbe..00000000000 --- a/.changeset/@graphql-codegen_gql-tag-operations-10218-dependencies.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@graphql-codegen/gql-tag-operations": patch ---- -dependencies updates: - - Updated dependency [`@graphql-codegen/visitor-plugin-common@5.6.1` ↗︎](https://www.npmjs.com/package/@graphql-codegen/visitor-plugin-common/v/5.6.1) (from `5.6.0`, in `dependencies`) diff --git a/.changeset/@graphql-codegen_graphql-modules-preset-10218-dependencies.md b/.changeset/@graphql-codegen_graphql-modules-preset-10218-dependencies.md deleted file mode 100644 index 1012f14b647..00000000000 --- a/.changeset/@graphql-codegen_graphql-modules-preset-10218-dependencies.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@graphql-codegen/graphql-modules-preset": patch ---- -dependencies updates: - - Updated dependency [`@graphql-codegen/visitor-plugin-common@5.6.1` ↗︎](https://www.npmjs.com/package/@graphql-codegen/visitor-plugin-common/v/5.6.1) (from `5.6.0`, in `dependencies`) diff --git a/.changeset/@graphql-codegen_typed-document-node-10218-dependencies.md b/.changeset/@graphql-codegen_typed-document-node-10218-dependencies.md deleted file mode 100644 index 968f5921f33..00000000000 --- a/.changeset/@graphql-codegen_typed-document-node-10218-dependencies.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@graphql-codegen/typed-document-node": patch ---- -dependencies updates: - - Updated dependency [`@graphql-codegen/visitor-plugin-common@5.6.1` ↗︎](https://www.npmjs.com/package/@graphql-codegen/visitor-plugin-common/v/5.6.1) (from `5.6.0`, in `dependencies`) diff --git a/.changeset/@graphql-codegen_typescript-10218-dependencies.md b/.changeset/@graphql-codegen_typescript-10218-dependencies.md deleted file mode 100644 index fb52b9eee9c..00000000000 --- a/.changeset/@graphql-codegen_typescript-10218-dependencies.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@graphql-codegen/typescript": patch ---- -dependencies updates: - - Updated dependency [`@graphql-codegen/visitor-plugin-common@5.6.1` ↗︎](https://www.npmjs.com/package/@graphql-codegen/visitor-plugin-common/v/5.6.1) (from `5.6.0`, in `dependencies`) diff --git a/.changeset/@graphql-codegen_typescript-document-nodes-10218-dependencies.md b/.changeset/@graphql-codegen_typescript-document-nodes-10218-dependencies.md deleted file mode 100644 index 51cb49e5535..00000000000 --- a/.changeset/@graphql-codegen_typescript-document-nodes-10218-dependencies.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -"@graphql-codegen/typescript-document-nodes": patch ---- -dependencies updates: - - Updated dependency [`@graphql-codegen/visitor-plugin-common@5.6.1` ↗︎](https://www.npmjs.com/package/@graphql-codegen/visitor-plugin-common/v/5.6.1) (from `5.6.0`, in `dependencies`) diff --git a/.changeset/@graphql-codegen_typescript-operations-10218-dependencies.md b/.changeset/@graphql-codegen_typescript-operations-10218-dependencies.md deleted file mode 100644 index 619352f5678..00000000000 --- a/.changeset/@graphql-codegen_typescript-operations-10218-dependencies.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@graphql-codegen/typescript-operations": patch ---- -dependencies updates: - - Updated dependency [`@graphql-codegen/typescript@^4.1.3` ↗︎](https://www.npmjs.com/package/@graphql-codegen/typescript/v/4.1.3) (from `^4.1.2`, in `dependencies`) - - Updated dependency [`@graphql-codegen/visitor-plugin-common@5.6.1` ↗︎](https://www.npmjs.com/package/@graphql-codegen/visitor-plugin-common/v/5.6.1) (from `5.6.0`, in `dependencies`) diff --git a/.changeset/@graphql-codegen_typescript-resolvers-10218-dependencies.md b/.changeset/@graphql-codegen_typescript-resolvers-10218-dependencies.md deleted file mode 100644 index bda76bce275..00000000000 --- a/.changeset/@graphql-codegen_typescript-resolvers-10218-dependencies.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -"@graphql-codegen/typescript-resolvers": patch ---- -dependencies updates: - - Updated dependency [`@graphql-codegen/typescript@^4.1.3` ↗︎](https://www.npmjs.com/package/@graphql-codegen/typescript/v/4.1.3) (from `^4.1.2`, in `dependencies`) - - Updated dependency [`@graphql-codegen/visitor-plugin-common@5.6.1` ↗︎](https://www.npmjs.com/package/@graphql-codegen/visitor-plugin-common/v/5.6.1) (from `5.6.0`, in `dependencies`)