Skip to content

Commit 6ed420a

Browse files
authored
CODEGEN-907 [typescript-resolvers] Fix Federation types not apply prefix and suffix (dotansimha#10797)
* Avoid nested describes * Fix FederationTypes not having typesPrefix and typesSuffix applied * Add changeset
1 parent 0e1297a commit 6ed420a

4 files changed

Lines changed: 267 additions & 65 deletions

File tree

.changeset/tangy-terms-dance.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
'@graphql-codegen/typescript-resolvers': patch
3+
'@graphql-codegen/plugin-helpers': patch
4+
---
5+
6+
Fix FederationTypes not having typesPrefix and typesSuffix applied

packages/plugins/typescript/resolvers/src/index.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ import {
66
PluginFunction,
77
Types,
88
} from '@graphql-codegen/plugin-helpers';
9-
import { parseMapper, type RootResolver } from '@graphql-codegen/visitor-plugin-common';
9+
import {
10+
convertFactory,
11+
convertName as convertNameUtil,
12+
parseMapper,
13+
type RootResolver,
14+
} from '@graphql-codegen/visitor-plugin-common';
1015
import { TypeScriptResolversPluginConfig } from './config.js';
1116
import { TypeScriptResolversVisitor } from './visitor.js';
1217

@@ -88,7 +93,19 @@ export type Resolver${capitalizedDirectiveName}WithResolve<TResult, TParent, TCo
8893
}
8994

9095
let { transformedSchema, federationMeta } = config.federation
91-
? addFederationReferencesToSchema(schema)
96+
? (() => {
97+
const baseConvert = convertFactory(config);
98+
return addFederationReferencesToSchema(schema, {
99+
convertName: name =>
100+
convertNameUtil({
101+
convert: () => baseConvert(name),
102+
options: {
103+
typesPrefix: config.typesPrefix || '',
104+
typesSuffix: config.typesSuffix || '',
105+
},
106+
}),
107+
});
108+
})()
92109
: { transformedSchema: schema, federationMeta: {} };
93110

94111
transformedSchema = config.customDirectives?.semanticNonNull

packages/plugins/typescript/resolvers/tests/ts-resolvers.federation.spec.ts

Lines changed: 234 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ function generate({ schema, config }: { schema: string; config: TypeScriptResolv
2323
});
2424
}
2525

26-
describe('TypeScript Resolvers Plugin + Apollo Federation', () => {
26+
describe('TypeScript Resolvers Plugin & Federation', () => {
2727
it('generates __resolveReference for object types with resolvable @key', async () => {
2828
const federatedSchema = /* GraphQL */ `
2929
type Query {
@@ -879,76 +879,77 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => {
879879
// no GraphQLScalarType
880880
expect(content).not.toContain('GraphQLScalarType');
881881
});
882+
});
882883

883-
describe('meta', () => {
884-
it('generates federation meta correctly', async () => {
885-
const federatedSchema = /* GraphQL */ `
886-
scalar _FieldSet
887-
directive @key(fields: _FieldSet!, resolvable: Boolean) repeatable on OBJECT | INTERFACE
884+
describe('TypeScript Resolvers Plugin & Federation - meta', () => {
885+
it('generates federation meta correctly', async () => {
886+
const federatedSchema = /* GraphQL */ `
887+
scalar _FieldSet
888+
directive @key(fields: _FieldSet!, resolvable: Boolean) repeatable on OBJECT | INTERFACE
888889
889-
type Query {
890-
user: UserPayload!
891-
allUsers: [User]
892-
}
890+
type Query {
891+
user: UserPayload!
892+
allUsers: [User]
893+
}
893894
894-
type User @key(fields: "id") {
895-
id: ID!
896-
name: String
897-
username: String
898-
}
895+
type User @key(fields: "id") {
896+
id: ID!
897+
name: String
898+
username: String
899+
}
899900
900-
interface Node {
901-
id: ID!
902-
}
901+
interface Node {
902+
id: ID!
903+
}
903904
904-
type UserOk {
905-
id: ID!
906-
}
907-
type UserError {
908-
message: String!
909-
}
910-
union UserPayload = UserOk | UserError
905+
type UserOk {
906+
id: ID!
907+
}
908+
type UserError {
909+
message: String!
910+
}
911+
union UserPayload = UserOk | UserError
911912
912-
enum Country {
913-
FR
914-
US
915-
}
913+
enum Country {
914+
FR
915+
US
916+
}
916917
917-
type NotResolvable @key(fields: "id", resolvable: false) {
918-
id: ID!
919-
}
918+
type NotResolvable @key(fields: "id", resolvable: false) {
919+
id: ID!
920+
}
920921
921-
type Resolvable @key(fields: "id", resolvable: true) {
922-
id: ID!
923-
}
922+
type Resolvable @key(fields: "id", resolvable: true) {
923+
id: ID!
924+
}
924925
925-
type MultipleResolvable
926-
@key(fields: "id")
927-
@key(fields: "id2", resolvable: true)
928-
@key(fields: "id3", resolvable: false) {
929-
id: ID!
930-
id2: ID!
931-
id3: ID!
932-
}
926+
type MultipleResolvable
927+
@key(fields: "id")
928+
@key(fields: "id2", resolvable: true)
929+
@key(fields: "id3", resolvable: false) {
930+
id: ID!
931+
id2: ID!
932+
id3: ID!
933+
}
933934
934-
type MultipleNonResolvable
935-
@key(fields: "id", resolvable: false)
936-
@key(fields: "id2", resolvable: false)
937-
@key(fields: "id3", resolvable: false) {
938-
id: ID!
939-
id2: ID!
940-
id3: ID!
941-
}
942-
`;
935+
type MultipleNonResolvable
936+
@key(fields: "id", resolvable: false)
937+
@key(fields: "id2", resolvable: false)
938+
@key(fields: "id3", resolvable: false) {
939+
id: ID!
940+
id2: ID!
941+
id3: ID!
942+
}
943+
`;
943944

944-
const result = await plugin(
945-
buildSchema(federatedSchema),
946-
[],
947-
{ federation: true },
948-
{ outputFile: '' },
949-
);
945+
const result = await plugin(
946+
buildSchema(federatedSchema),
947+
[],
948+
{ federation: true },
949+
{ outputFile: '' },
950+
);
950951

951-
expect(result.meta?.generatedResolverTypes).toMatchInlineSnapshot(`
952+
expect(result.meta?.generatedResolverTypes).toMatchInlineSnapshot(`
952953
{
953954
"resolversMap": {
954955
"name": "Resolvers",
@@ -1006,6 +1007,179 @@ describe('TypeScript Resolvers Plugin + Apollo Federation', () => {
10061007
},
10071008
}
10081009
`);
1009-
});
1010+
});
1011+
});
1012+
1013+
describe('TypeScript Resolvers Plugin & Federation - config', () => {
1014+
it('generates FederationTypes with correct typesPrefix and typesSuffix applied', async () => {
1015+
const federatedSchema = /* GraphQL */ `
1016+
scalar _FieldSet
1017+
directive @key(fields: _FieldSet!, resolvable: Boolean) repeatable on OBJECT | INTERFACE
1018+
1019+
type Query {
1020+
all: [Person]
1021+
}
1022+
1023+
interface Person @key(fields: "id") {
1024+
id: ID!
1025+
}
1026+
1027+
type User @key(fields: "id") {
1028+
id: ID!
1029+
name: String
1030+
}
1031+
`;
1032+
1033+
const result = await plugin(
1034+
buildSchema(federatedSchema),
1035+
[],
1036+
{ federation: true, typesPrefix: '$', typesSuffix: '__' },
1037+
{ outputFile: '' },
1038+
);
1039+
1040+
expect(result.content).toMatchInlineSnapshot(`
1041+
"
1042+
1043+
export type ResolverTypeWrapper<T> = Promise<T> | T;
1044+
1045+
export type ReferenceResolver<TResult, TReference, TContext> = (
1046+
reference: TReference,
1047+
context: TContext,
1048+
info: GraphQLResolveInfo
1049+
) => Promise<TResult> | TResult;
1050+
1051+
type ScalarCheck<T, S> = S extends true ? T : NullableCheck<T, S>;
1052+
type NullableCheck<T, S> = Maybe<T> extends T ? Maybe<ListCheck<NonNullable<T>, S>> : ListCheck<T, S>;
1053+
type ListCheck<T, S> = T extends (infer U)[] ? NullableCheck<U, S>[] : GraphQLRecursivePick<T, S>;
1054+
export type GraphQLRecursivePick<T, S> = { [K in keyof T & keyof S]: ScalarCheck<T[K], S[K]> };
1055+
1056+
1057+
export type ResolverWithResolve<TResult, TParent, TContext, TArgs> = {
1058+
resolve: ResolverFn<TResult, TParent, TContext, TArgs>;
1059+
};
1060+
export type Resolver<TResult, TParent = Record<PropertyKey, never>, TContext = Record<PropertyKey, never>, TArgs = Record<PropertyKey, never>> = ResolverFn<TResult, TParent, TContext, TArgs> | ResolverWithResolve<TResult, TParent, TContext, TArgs>;
1061+
1062+
export type ResolverFn<TResult, TParent, TContext, TArgs> = (
1063+
parent: TParent,
1064+
args: TArgs,
1065+
context: TContext,
1066+
info: GraphQLResolveInfo
1067+
) => Promise<TResult> | TResult;
1068+
1069+
export type SubscriptionSubscribeFn<TResult, TParent, TContext, TArgs> = (
1070+
parent: TParent,
1071+
args: TArgs,
1072+
context: TContext,
1073+
info: GraphQLResolveInfo
1074+
) => AsyncIterable<TResult> | Promise<AsyncIterable<TResult>>;
1075+
1076+
export type SubscriptionResolveFn<TResult, TParent, TContext, TArgs> = (
1077+
parent: TParent,
1078+
args: TArgs,
1079+
context: TContext,
1080+
info: GraphQLResolveInfo
1081+
) => TResult | Promise<TResult>;
1082+
1083+
export interface SubscriptionSubscriberObject<TResult, TKey extends string, TParent, TContext, TArgs> {
1084+
subscribe: SubscriptionSubscribeFn<{ [key in TKey]: TResult }, TParent, TContext, TArgs>;
1085+
resolve?: SubscriptionResolveFn<TResult, { [key in TKey]: TResult }, TContext, TArgs>;
1086+
}
1087+
1088+
export interface SubscriptionResolverObject<TResult, TParent, TContext, TArgs> {
1089+
subscribe: SubscriptionSubscribeFn<any, TParent, TContext, TArgs>;
1090+
resolve: SubscriptionResolveFn<TResult, any, TContext, TArgs>;
1091+
}
1092+
1093+
export type SubscriptionObject<TResult, TKey extends string, TParent, TContext, TArgs> =
1094+
| SubscriptionSubscriberObject<TResult, TKey, TParent, TContext, TArgs>
1095+
| SubscriptionResolverObject<TResult, TParent, TContext, TArgs>;
1096+
1097+
export type SubscriptionResolver<TResult, TKey extends string, TParent = Record<PropertyKey, never>, TContext = Record<PropertyKey, never>, TArgs = Record<PropertyKey, never>> =
1098+
| ((...args: any[]) => SubscriptionObject<TResult, TKey, TParent, TContext, TArgs>)
1099+
| SubscriptionObject<TResult, TKey, TParent, TContext, TArgs>;
1100+
1101+
export type TypeResolveFn<TTypes, TParent = Record<PropertyKey, never>, TContext = Record<PropertyKey, never>> = (
1102+
parent: TParent,
1103+
context: TContext,
1104+
info: GraphQLResolveInfo
1105+
) => Maybe<TTypes> | Promise<Maybe<TTypes>>;
1106+
1107+
export type IsTypeOfResolverFn<T = Record<PropertyKey, never>, TContext = Record<PropertyKey, never>> = (obj: T, context: TContext, info: GraphQLResolveInfo) => boolean | Promise<boolean>;
1108+
1109+
export type NextResolverFn<T> = () => Promise<T>;
1110+
1111+
export type DirectiveResolverFn<TResult = Record<PropertyKey, never>, TParent = Record<PropertyKey, never>, TContext = Record<PropertyKey, never>, TArgs = Record<PropertyKey, never>> = (
1112+
next: NextResolverFn<TResult>,
1113+
parent: TParent,
1114+
args: TArgs,
1115+
context: TContext,
1116+
info: GraphQLResolveInfo
1117+
) => TResult | Promise<TResult>;
1118+
1119+
/** Mapping of federation types */
1120+
export type $FederationTypes__ = {
1121+
Person: $Person__;
1122+
User: $User__;
1123+
};
1124+
1125+
/** Mapping of federation reference types */
1126+
export type $FederationReferenceTypes__ = {
1127+
Person:
1128+
( { __typename: 'Person' }
1129+
& GraphQLRecursivePick<$FederationTypes__['Person'], {"id":true}> );
1130+
User:
1131+
( { __typename: 'User' }
1132+
& GraphQLRecursivePick<$FederationTypes__['User'], {"id":true}> );
1133+
};
1134+
1135+
1136+
/** Mapping of interface types */
1137+
export type $ResolversInterfaceTypes__<_RefType extends Record<string, unknown>> = {
1138+
Person: never;
1139+
};
1140+
1141+
/** Mapping between all available schema types and the resolvers types */
1142+
export type $ResolversTypes__ = {
1143+
Query: ResolverTypeWrapper<Record<PropertyKey, never>>;
1144+
Person: ResolverTypeWrapper<$ResolversInterfaceTypes__<$ResolversTypes__>['Person']>;
1145+
ID: ResolverTypeWrapper<Scalars['ID']['output']>;
1146+
User: ResolverTypeWrapper<$User__>;
1147+
String: ResolverTypeWrapper<Scalars['String']['output']>;
1148+
Boolean: ResolverTypeWrapper<Scalars['Boolean']['output']>;
1149+
};
1150+
1151+
/** Mapping between all available schema types and the resolvers parents */
1152+
export type $ResolversParentTypes__ = {
1153+
Query: Record<PropertyKey, never>;
1154+
Person: $ResolversInterfaceTypes__<$ResolversParentTypes__>['Person'];
1155+
ID: Scalars['ID']['output'];
1156+
User: $User__ | $FederationReferenceTypes__['User'];
1157+
String: Scalars['String']['output'];
1158+
Boolean: Scalars['Boolean']['output'];
1159+
};
1160+
1161+
export type $QueryResolvers__<ContextType = any, ParentType extends $ResolversParentTypes__['Query'] = $ResolversParentTypes__['Query']> = {
1162+
all?: Resolver<Maybe<Array<Maybe<$ResolversTypes__['Person']>>>, ParentType, ContextType>;
1163+
};
1164+
1165+
export type $PersonResolvers__<ContextType = any, ParentType extends $ResolversParentTypes__['Person'] = $ResolversParentTypes__['Person'], FederationReferenceType extends $FederationReferenceTypes__['Person'] = $FederationReferenceTypes__['Person']> = {
1166+
__resolveType: TypeResolveFn<null, ParentType, ContextType>;
1167+
__resolveReference?: ReferenceResolver<Maybe<$ResolversTypes__['Person']> | FederationReferenceType, FederationReferenceType, ContextType>;
1168+
};
1169+
1170+
export type $UserResolvers__<ContextType = any, ParentType extends $ResolversParentTypes__['User'] = $ResolversParentTypes__['User'], FederationReferenceType extends $FederationReferenceTypes__['User'] = $FederationReferenceTypes__['User']> = {
1171+
__resolveReference?: ReferenceResolver<Maybe<$ResolversTypes__['User']> | FederationReferenceType, FederationReferenceType, ContextType>;
1172+
id?: Resolver<$ResolversTypes__['ID'], ParentType, ContextType>;
1173+
name?: Resolver<Maybe<$ResolversTypes__['String']>, ParentType, ContextType>;
1174+
};
1175+
1176+
export type $Resolvers__<ContextType = any> = {
1177+
Query?: $QueryResolvers__<ContextType>;
1178+
Person?: $PersonResolvers__<ContextType>;
1179+
User?: $UserResolvers__<ContextType>;
1180+
};
1181+
1182+
"
1183+
`);
10101184
});
10111185
});

packages/utils/plugins-helpers/src/federation.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,12 @@ export type FederationMeta = { [typeName: string]: TypeMeta };
7979
* - return type
8080
* @param schema
8181
*/
82-
export function addFederationReferencesToSchema(schema: GraphQLSchema): {
82+
export function addFederationReferencesToSchema(
83+
schema: GraphQLSchema,
84+
config: {
85+
convertName: (type: string) => string;
86+
},
87+
): {
8388
transformedSchema: GraphQLSchema;
8489
federationMeta: FederationMeta;
8590
} {
@@ -275,7 +280,7 @@ export function addFederationReferencesToSchema(schema: GraphQLSchema): {
275280

276281
const referenceSelectionSetsString = printReferenceSelectionSets({
277282
typeName: type.name,
278-
baseFederationType: `FederationTypes['${type.name}']`, // FIXME: run convertName on FederationTypes
283+
baseFederationType: `${config.convertName('FederationTypes')}['${type.name}']`,
279284
referenceSelectionSets,
280285
});
281286

@@ -315,7 +320,7 @@ export function addFederationReferencesToSchema(schema: GraphQLSchema): {
315320

316321
const referenceSelectionSetsString = printReferenceSelectionSets({
317322
typeName: type.name,
318-
baseFederationType: `FederationTypes['${type.name}']`, // FIXME: run convertName on FederationTypes
323+
baseFederationType: `${config.convertName('FederationTypes')}['${type.name}']`,
319324
referenceSelectionSets,
320325
});
321326

0 commit comments

Comments
 (0)