Skip to content

Commit 7a93bba

Browse files
committed
RFC: SchemaExtension
This adds support for graphql/graphql-spec#428 spec proposal. So far this just adds language support and updates validation rules to be aware of this new ast node. I'll follow up with support in `extendSchema()` and tests.
1 parent 3cb4500 commit 7a93bba

11 files changed

+93
-19
lines changed

src/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,14 +245,16 @@ export type {
245245
EnumTypeDefinitionNode,
246246
EnumValueDefinitionNode,
247247
InputObjectTypeDefinitionNode,
248+
DirectiveDefinitionNode,
249+
TypeSystemExtensionNode,
250+
SchemaExtensionNode,
248251
TypeExtensionNode,
249252
ScalarTypeExtensionNode,
250253
ObjectTypeExtensionNode,
251254
InterfaceTypeExtensionNode,
252255
UnionTypeExtensionNode,
253256
EnumTypeExtensionNode,
254257
InputObjectTypeExtensionNode,
255-
DirectiveDefinitionNode,
256258
KindEnum,
257259
TokenKindEnum,
258260
DirectiveLocationEnum,

src/language/__tests__/schema-kitchen-sink.graphql

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,9 @@ directive @include2(if: Boolean!) on
123123
| FIELD
124124
| FRAGMENT_SPREAD
125125
| INLINE_FRAGMENT
126+
127+
extend schema @onSchema
128+
129+
extend schema @onSchema {
130+
subscription: SubscriptionType
131+
}

src/language/__tests__/schema-printer-test.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,12 @@ describe('Printer: SDL document', () => {
161161
directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
162162
163163
directive @include2(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT
164+
165+
extend schema @onSchema
166+
167+
extend schema @onSchema {
168+
subscription: SubscriptionType
169+
}
164170
`);
165171
});
166172
});

src/language/ast.js

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ export type ASTNode =
124124
| EnumTypeDefinitionNode
125125
| EnumValueDefinitionNode
126126
| InputObjectTypeDefinitionNode
127+
| SchemaExtensionNode
127128
| ScalarTypeExtensionNode
128129
| ObjectTypeExtensionNode
129130
| InterfaceTypeExtensionNode
@@ -171,6 +172,7 @@ export type ASTKindToNode = {
171172
EnumTypeDefinition: EnumTypeDefinitionNode,
172173
EnumValueDefinition: EnumValueDefinitionNode,
173174
InputObjectTypeDefinition: InputObjectTypeDefinitionNode,
175+
SchemaExtension: SchemaExtensionNode,
174176
ScalarTypeExtension: ScalarTypeExtensionNode,
175177
ObjectTypeExtension: ObjectTypeExtensionNode,
176178
InterfaceTypeExtension: InterfaceTypeExtensionNode,
@@ -388,7 +390,7 @@ export type NonNullTypeNode = {
388390
export type TypeSystemDefinitionNode =
389391
| SchemaDefinitionNode
390392
| TypeDefinitionNode
391-
| TypeExtensionNode
393+
| TypeSystemExtensionNode
392394
| DirectiveDefinitionNode;
393395

394396
export type SchemaDefinitionNode = {
@@ -497,6 +499,28 @@ export type InputObjectTypeDefinitionNode = {
497499
+fields?: $ReadOnlyArray<InputValueDefinitionNode>,
498500
};
499501

502+
// Directive Definitions
503+
504+
export type DirectiveDefinitionNode = {
505+
+kind: 'DirectiveDefinition',
506+
+loc ?: Location,
507+
+description ?: StringValueNode,
508+
+name: NameNode,
509+
+arguments ?: $ReadOnlyArray < InputValueDefinitionNode >,
510+
+locations: $ReadOnlyArray < NameNode >,
511+
};
512+
513+
// Type System Extensions
514+
515+
export type TypeSystemExtensionNode = SchemaExtensionNode | TypeExtensionNode;
516+
517+
export type SchemaExtensionNode = {
518+
+kind: 'SchemaExtension',
519+
+loc?: Location,
520+
+directives: $ReadOnlyArray<DirectiveNode>,
521+
+operationTypes: $ReadOnlyArray<OperationTypeDefinitionNode>,
522+
};
523+
500524
// Type Extensions
501525

502526
export type TypeExtensionNode =
@@ -554,14 +578,3 @@ export type InputObjectTypeExtensionNode = {
554578
+directives?: $ReadOnlyArray<DirectiveNode>,
555579
+fields?: $ReadOnlyArray<InputValueDefinitionNode>,
556580
};
557-
558-
// Directive Definitions
559-
560-
export type DirectiveDefinitionNode = {
561-
+kind: 'DirectiveDefinition',
562-
+loc?: Location,
563-
+description?: StringValueNode,
564-
+name: NameNode,
565-
+arguments?: $ReadOnlyArray<InputValueDefinitionNode>,
566-
+locations: $ReadOnlyArray<NameNode>,
567-
};

src/language/index.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,16 @@ export type {
7676
EnumTypeDefinitionNode,
7777
EnumValueDefinitionNode,
7878
InputObjectTypeDefinitionNode,
79+
DirectiveDefinitionNode,
80+
TypeSystemExtensionNode,
81+
SchemaExtensionNode,
7982
TypeExtensionNode,
8083
ScalarTypeExtensionNode,
8184
ObjectTypeExtensionNode,
8285
InterfaceTypeExtensionNode,
8386
UnionTypeExtensionNode,
8487
EnumTypeExtensionNode,
8588
InputObjectTypeExtensionNode,
86-
DirectiveDefinitionNode,
8789
} from './ast';
8890

8991
export { DirectiveLocation } from './directiveLocation';

src/language/kinds.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,9 @@ export const Kind = Object.freeze({
6262
ENUM_VALUE_DEFINITION: 'EnumValueDefinition',
6363
INPUT_OBJECT_TYPE_DEFINITION: 'InputObjectTypeDefinition',
6464

65+
// Type System Extensions
66+
SCHEMA_EXTENSION: 'SchemaExtension',
67+
6568
// Type Extensions
6669
SCALAR_TYPE_EXTENSION: 'ScalarTypeExtension',
6770
OBJECT_TYPE_EXTENSION: 'ObjectTypeExtension',

src/language/parser.js

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ import type {
5252
EnumTypeDefinitionNode,
5353
EnumValueDefinitionNode,
5454
InputObjectTypeDefinitionNode,
55-
TypeExtensionNode,
55+
TypeSystemExtensionNode,
56+
SchemaExtensionNode,
5657
ScalarTypeExtensionNode,
5758
ObjectTypeExtensionNode,
5859
InterfaceTypeExtensionNode,
@@ -751,9 +752,9 @@ export function parseNamedType(lexer: Lexer<*>): NamedTypeNode {
751752

752753
/**
753754
* TypeSystemDefinition :
755+
* - TypeSystemExtension
754756
* - SchemaDefinition
755757
* - TypeDefinition
756-
* - TypeExtension
757758
* - DirectiveDefinition
758759
*
759760
* TypeDefinition :
@@ -785,7 +786,7 @@ function parseTypeSystemDefinition(lexer: Lexer<*>): TypeSystemDefinitionNode {
785786
case 'input':
786787
return parseInputObjectTypeDefinition(lexer);
787788
case 'extend':
788-
return parseTypeExtension(lexer);
789+
return parseTypeSystemExtension(lexer);
789790
case 'directive':
790791
return parseDirectiveDefinition(lexer);
791792
}
@@ -1141,6 +1142,10 @@ function parseInputFieldsDefinition(
11411142
}
11421143

11431144
/**
1145+
* TypeSystemExtension :
1146+
* - SchemaExtension
1147+
* - TypeExtension
1148+
*
11441149
* TypeExtension :
11451150
* - ScalarTypeExtension
11461151
* - ObjectTypeExtension
@@ -1149,11 +1154,13 @@ function parseInputFieldsDefinition(
11491154
* - EnumTypeExtension
11501155
* - InputObjectTypeDefinition
11511156
*/
1152-
function parseTypeExtension(lexer: Lexer<*>): TypeExtensionNode {
1157+
function parseTypeSystemExtension(lexer: Lexer<*>): TypeSystemExtensionNode {
11531158
const keywordToken = lexer.lookahead();
11541159

11551160
if (keywordToken.kind === TokenKind.NAME) {
11561161
switch (keywordToken.value) {
1162+
case 'schema':
1163+
return parseSchemaExtension(lexer);
11571164
case 'scalar':
11581165
return parseScalarTypeExtension(lexer);
11591166
case 'type':
@@ -1172,6 +1179,35 @@ function parseTypeExtension(lexer: Lexer<*>): TypeExtensionNode {
11721179
throw unexpected(lexer, keywordToken);
11731180
}
11741181

1182+
/**
1183+
* SchemaExtension :
1184+
* - extend schema Directives[Const]? { OperationTypeDefinition+ }
1185+
* - extend schema Directives[Const]
1186+
*/
1187+
function parseSchemaExtension(lexer: Lexer<*>): SchemaExtensionNode {
1188+
const start = lexer.token;
1189+
expectKeyword(lexer, 'extend');
1190+
expectKeyword(lexer, 'schema');
1191+
const directives = parseDirectives(lexer, true);
1192+
const operationTypes = peek(lexer, TokenKind.BRACE_L)
1193+
? many(
1194+
lexer,
1195+
TokenKind.BRACE_L,
1196+
parseOperationTypeDefinition,
1197+
TokenKind.BRACE_R,
1198+
)
1199+
: [];
1200+
if (directives.length === 0 && operationTypes.length === 0) {
1201+
throw unexpected(lexer);
1202+
}
1203+
return {
1204+
kind: Kind.SCHEMA_EXTENSION,
1205+
directives,
1206+
operationTypes,
1207+
loc: loc(lexer, start),
1208+
};
1209+
}
1210+
11751211
/**
11761212
* ScalarTypeExtension :
11771213
* - extend scalar Name Directives[Const]

src/language/printer.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,9 @@ const printDocASTReducer = {
175175
join(['input', name, join(directives, ' '), block(fields)], ' '),
176176
),
177177

178+
SchemaExtension: ({ directives, operationTypes }) =>
179+
join(['extend schema', join(directives, ' '), block(operationTypes)], ' '),
180+
178181
ScalarTypeExtension: ({ name, directives }) =>
179182
join(['extend scalar', name, join(directives, ' ')], ' '),
180183

src/language/visitor.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ export const QueryDocumentKeys = {
9999
NonNullType: ['type'],
100100

101101
SchemaDefinition: ['directives', 'operationTypes'],
102+
SchemaExtension: ['directives', 'operationTypes'],
102103
OperationTypeDefinition: ['type'],
103104

104105
ScalarTypeDefinition: ['description', 'name', 'directives'],

src/validation/rules/ExecutableDefinitions.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ export function ExecutableDefinitions(context: ValidationContext): ASTVisitor {
3333
context.reportError(
3434
new GraphQLError(
3535
nonExecutableDefinitionMessage(
36-
definition.kind === Kind.SCHEMA_DEFINITION
36+
definition.kind === Kind.SCHEMA_DEFINITION ||
37+
definition.kind === Kind.SCHEMA_EXTENSION
3738
? 'schema'
3839
: definition.name.value,
3940
),

src/validation/rules/KnownDirectives.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ function getDirectiveLocationForASTPath(ancestors) {
8383
case Kind.FRAGMENT_DEFINITION:
8484
return DirectiveLocation.FRAGMENT_DEFINITION;
8585
case Kind.SCHEMA_DEFINITION:
86+
case Kind.SCHEMA_EXTENSION:
8687
return DirectiveLocation.SCHEMA;
8788
case Kind.SCALAR_TYPE_DEFINITION:
8889
case Kind.SCALAR_TYPE_EXTENSION:

0 commit comments

Comments
 (0)