Skip to content

Commit 6715330

Browse files
authored
fix(visitor-plugin-common): handle schema extension nodes (#10498)
1 parent aad7f03 commit 6715330

File tree

7 files changed

+211
-9
lines changed

7 files changed

+211
-9
lines changed

.changeset/tender-papayas-stare.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
'@graphql-codegen/visitor-plugin-common': patch
3+
---
4+
5+
Handle schema extension nodes correctly
6+
7+
When a schema doesn't have an operation type defined but has `schema extension` definitions with directives like below,
8+
schema extensions are not converted to schema definitions by GraphQL Tools.
9+
So the visitor should handle schema extension nodes correctly.
10+
11+
Follow-up to https://github.com/ardatan/graphql-tools/pull/7679
12+
13+
```graphql
14+
extend schema @link(url: "https://specs.apollo.dev/federation/v2.0", import: ["@key"])
15+
16+
type Foo {
17+
id: ID! @key
18+
name: String
19+
}
20+
```

dev-test/codegen.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,19 @@ const config: CodegenConfig = {
243243
},
244244
},
245245
},
246+
'./dev-test/test-federation/generated/types.ts': {
247+
schema: './dev-test/test-federation/schema.gql',
248+
plugins: ['typescript', 'typescript-resolvers'],
249+
config: {
250+
mapperTypeSuffix: 'Mapper',
251+
enumsAsTypes: true,
252+
useIndexSignature: true,
253+
maybeValue: 'T | null | undefined',
254+
scalars: {
255+
CarKey: 'string',
256+
},
257+
},
258+
},
246259
},
247260
};
248261

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
import { GraphQLResolveInfo, GraphQLScalarType, GraphQLScalarTypeConfig } from 'graphql';
2+
export type Maybe<T> = T | null | undefined;
3+
export type InputMaybe<T> = T | null | undefined;
4+
export type Exact<T extends { [key: string]: unknown }> = { [K in keyof T]: T[K] };
5+
export type MakeOptional<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]?: Maybe<T[SubKey]> };
6+
export type MakeMaybe<T, K extends keyof T> = Omit<T, K> & { [SubKey in K]: Maybe<T[SubKey]> };
7+
export type MakeEmpty<T extends { [key: string]: unknown }, K extends keyof T> = { [_ in K]?: never };
8+
export type Incremental<T> = T | { [P in keyof T]?: P extends ' $fragmentName' | '__typename' ? T[P] : never };
9+
/** All built-in and custom scalars, mapped to their actual values */
10+
export type Scalars = {
11+
ID: { input: string; output: string };
12+
String: { input: string; output: string };
13+
Boolean: { input: boolean; output: boolean };
14+
Int: { input: number; output: number };
15+
Float: { input: number; output: number };
16+
/** Represents a car */
17+
CarKey: { input: string; output: string };
18+
};
19+
20+
export type Car = {
21+
__typename?: 'Car';
22+
carKey: Scalars['CarKey']['output'];
23+
/** Extend Car with a simple "dummy" field */
24+
dummy?: Maybe<Scalars['String']['output']>;
25+
};
26+
27+
export type WithIndex<TObject> = TObject & Record<string, any>;
28+
export type ResolversObject<TObject> = WithIndex<TObject>;
29+
30+
export type ResolverTypeWrapper<T> = Promise<T> | T;
31+
32+
export type ResolverWithResolve<TResult, TParent, TContext, TArgs> = {
33+
resolve: ResolverFn<TResult, TParent, TContext, TArgs>;
34+
};
35+
export type Resolver<
36+
TResult,
37+
TParent = Record<PropertyKey, never>,
38+
TContext = Record<PropertyKey, never>,
39+
TArgs = Record<PropertyKey, never>
40+
> = ResolverFn<TResult, TParent, TContext, TArgs> | ResolverWithResolve<TResult, TParent, TContext, TArgs>;
41+
42+
export type ResolverFn<TResult, TParent, TContext, TArgs> = (
43+
parent: TParent,
44+
args: TArgs,
45+
context: TContext,
46+
info: GraphQLResolveInfo
47+
) => Promise<TResult> | TResult;
48+
49+
export type SubscriptionSubscribeFn<TResult, TParent, TContext, TArgs> = (
50+
parent: TParent,
51+
args: TArgs,
52+
context: TContext,
53+
info: GraphQLResolveInfo
54+
) => AsyncIterable<TResult> | Promise<AsyncIterable<TResult>>;
55+
56+
export type SubscriptionResolveFn<TResult, TParent, TContext, TArgs> = (
57+
parent: TParent,
58+
args: TArgs,
59+
context: TContext,
60+
info: GraphQLResolveInfo
61+
) => TResult | Promise<TResult>;
62+
63+
export interface SubscriptionSubscriberObject<TResult, TKey extends string, TParent, TContext, TArgs> {
64+
subscribe: SubscriptionSubscribeFn<{ [key in TKey]: TResult }, TParent, TContext, TArgs>;
65+
resolve?: SubscriptionResolveFn<TResult, { [key in TKey]: TResult }, TContext, TArgs>;
66+
}
67+
68+
export interface SubscriptionResolverObject<TResult, TParent, TContext, TArgs> {
69+
subscribe: SubscriptionSubscribeFn<any, TParent, TContext, TArgs>;
70+
resolve: SubscriptionResolveFn<TResult, any, TContext, TArgs>;
71+
}
72+
73+
export type SubscriptionObject<TResult, TKey extends string, TParent, TContext, TArgs> =
74+
| SubscriptionSubscriberObject<TResult, TKey, TParent, TContext, TArgs>
75+
| SubscriptionResolverObject<TResult, TParent, TContext, TArgs>;
76+
77+
export type SubscriptionResolver<
78+
TResult,
79+
TKey extends string,
80+
TParent = Record<PropertyKey, never>,
81+
TContext = Record<PropertyKey, never>,
82+
TArgs = Record<PropertyKey, never>
83+
> =
84+
| ((...args: any[]) => SubscriptionObject<TResult, TKey, TParent, TContext, TArgs>)
85+
| SubscriptionObject<TResult, TKey, TParent, TContext, TArgs>;
86+
87+
export type TypeResolveFn<TTypes, TParent = Record<PropertyKey, never>, TContext = Record<PropertyKey, never>> = (
88+
parent: TParent,
89+
context: TContext,
90+
info: GraphQLResolveInfo
91+
) => Maybe<TTypes> | Promise<Maybe<TTypes>>;
92+
93+
export type IsTypeOfResolverFn<T = Record<PropertyKey, never>, TContext = Record<PropertyKey, never>> = (
94+
obj: T,
95+
context: TContext,
96+
info: GraphQLResolveInfo
97+
) => boolean | Promise<boolean>;
98+
99+
export type NextResolverFn<T> = () => Promise<T>;
100+
101+
export type DirectiveResolverFn<
102+
TResult = Record<PropertyKey, never>,
103+
TParent = Record<PropertyKey, never>,
104+
TContext = Record<PropertyKey, never>,
105+
TArgs = Record<PropertyKey, never>
106+
> = (
107+
next: NextResolverFn<TResult>,
108+
parent: TParent,
109+
args: TArgs,
110+
context: TContext,
111+
info: GraphQLResolveInfo
112+
) => TResult | Promise<TResult>;
113+
114+
/** Mapping between all available schema types and the resolvers types */
115+
export type ResolversTypes = ResolversObject<{
116+
Boolean: ResolverTypeWrapper<Scalars['Boolean']['output']>;
117+
Car: ResolverTypeWrapper<Car>;
118+
CarKey: ResolverTypeWrapper<Scalars['CarKey']['output']>;
119+
String: ResolverTypeWrapper<Scalars['String']['output']>;
120+
}>;
121+
122+
/** Mapping between all available schema types and the resolvers parents */
123+
export type ResolversParentTypes = ResolversObject<{
124+
Boolean: Scalars['Boolean']['output'];
125+
Car: Car;
126+
CarKey: Scalars['CarKey']['output'];
127+
String: Scalars['String']['output'];
128+
}>;
129+
130+
export type CarResolvers<
131+
ContextType = any,
132+
ParentType extends ResolversParentTypes['Car'] = ResolversParentTypes['Car']
133+
> = ResolversObject<{
134+
carKey?: Resolver<ResolversTypes['CarKey'], ParentType, ContextType>;
135+
dummy?: Resolver<Maybe<ResolversTypes['String']>, ParentType, ContextType>;
136+
}>;
137+
138+
export interface CarKeyScalarConfig extends GraphQLScalarTypeConfig<ResolversTypes['CarKey'], any> {
139+
name: 'CarKey';
140+
}
141+
142+
export type Resolvers<ContextType = any> = ResolversObject<{
143+
Car?: CarResolvers<ContextType>;
144+
CarKey?: GraphQLScalarType;
145+
}>;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
extend schema
2+
@link(url: "https://specs.apollo.dev/link/v1.0")
3+
@link(url: "https://specs.apollo.dev/federation/v2.6", import: ["@key", "@extends"])
4+
5+
type Car @extends @key(fields: "carKey") {
6+
carKey: CarKey!
7+
"""
8+
Extend Car with a simple "dummy" field
9+
"""
10+
dummy: String
11+
}
12+
13+
"""
14+
Represents a car
15+
"""
16+
scalar CarKey

packages/plugins/other/visitor-plugin-common/src/base-resolvers-visitor.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2067,6 +2067,10 @@ export class BaseResolversVisitor<
20672067
return null;
20682068
}
20692069

2070+
SchemaExtension() {
2071+
return null;
2072+
}
2073+
20702074
private getRelevantFieldsToOmit({
20712075
schemaType,
20722076
shouldInclude,

packages/plugins/other/visitor-plugin-common/src/base-types-visitor.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,6 +1046,10 @@ export class BaseTypesVisitor<
10461046
return null;
10471047
}
10481048

1049+
SchemaExtension() {
1050+
return null;
1051+
}
1052+
10491053
getNodeComment(node: FieldDefinitionNode | EnumValueDefinitionNode | InputValueDefinitionNode): string {
10501054
let commentText = node.description?.value;
10511055
const deprecationDirective = node.directives.find(v => v.name.value === 'deprecated');

yarn.lock

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2998,9 +2998,9 @@
29982998
ws "^8.17.1"
29992999

30003000
"@graphql-tools/utils@^10.0.0", "@graphql-tools/utils@^10.0.13", "@graphql-tools/utils@^10.10.0", "@graphql-tools/utils@^10.10.1", "@graphql-tools/utils@^10.3.2", "@graphql-tools/utils@^10.5.4", "@graphql-tools/utils@^10.8.6":
3001-
version "10.10.1"
3002-
resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-10.10.1.tgz#eeec3bdc5f5521b8b11c5148cdf0c8affb4f9aed"
3003-
integrity sha512-9iOZ7x6tuIpp/dviNmTCSH1cDDNLIcrj6T3WKH9lU4nRWx5Pr0e7Faj7T/HmP2Njrjik63dJWuDVRxfQSTOc4g==
3001+
version "10.10.2"
3002+
resolved "https://registry.yarnpkg.com/@graphql-tools/utils/-/utils-10.10.2.tgz#2edeb1672dfb0183a23145453ea73c89d1a013c6"
3003+
integrity sha512-aVPIAsZ8PMomO2UODO+uG8YCwYOfPthHO2b8pXqixlXx01L0B01qGkrQ0KYJDI/gozNNFXiZ3TfoFMXSGnPiow==
30043004
dependencies:
30053005
"@graphql-typed-document-node/core" "^3.1.1"
30063006
"@whatwg-node/promise-helpers" "^1.0.0"
@@ -4890,16 +4890,16 @@
48904890
resolved "https://registry.yarnpkg.com/@types/trusted-types/-/trusted-types-2.0.2.tgz#fc25ad9943bcac11cceb8168db4f275e0e72e756"
48914891
integrity sha512-F5DIZ36YVLE+PN+Zwws4kJogq47hNgX3Nx6WyDJ3kcplxyke3XIzB8uK5n/Lpm1HBsbGzd6nmGehL8cPekP+Tg==
48924892

4893-
"@types/unist@*", "@types/unist@^2", "@types/unist@^2.0.0", "@types/unist@^2.0.2":
4894-
version "2.0.11"
4895-
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.11.tgz#11af57b127e32487774841f7a4e54eab166d03c4"
4896-
integrity sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==
4897-
4898-
"@types/unist@^3.0.0":
4893+
"@types/unist@*", "@types/unist@^3.0.0":
48994894
version "3.0.0"
49004895
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.0.tgz#988ae8af1e5239e89f9fbb1ade4c935f4eeedf9a"
49014896
integrity sha512-MFETx3tbTjE7Uk6vvnWINA/1iJ7LuMdO4fcq8UfF0pRbj01aGLduVvQcRyswuACJdpnHgg8E3rQLhaRdNEJS0w==
49024897

4898+
"@types/unist@^2", "@types/unist@^2.0.0", "@types/unist@^2.0.2":
4899+
version "2.0.11"
4900+
resolved "https://registry.yarnpkg.com/@types/unist/-/unist-2.0.11.tgz#11af57b127e32487774841f7a4e54eab166d03c4"
4901+
integrity sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==
4902+
49034903
"@types/ws@^8.0.0":
49044904
version "8.5.4"
49054905
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.4.tgz#bb10e36116d6e570dd943735f86c933c1587b8a5"

0 commit comments

Comments
 (0)