Skip to content

Commit a830bb1

Browse files
committed
Added support for client defined required field
1 parent 1611bbb commit a830bb1

4 files changed

Lines changed: 35 additions & 17 deletions

File tree

src/execution/execute.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import type {
3838
GraphQLResolveInfo,
3939
GraphQLTypeResolver,
4040
GraphQLList,
41+
GraphQLNonNull,
4142
} from '../type/definition';
4243
import { assertValidSchema } from '../type/validate';
4344
import {
@@ -640,7 +641,8 @@ function resolveField(
640641
return;
641642
}
642643

643-
const returnType = fieldDef.type;
644+
const returnType = fieldNode.required ? new GraphQLNonNull(fieldDef.type) : fieldDef.type
645+
644646
const resolveFn = fieldDef.resolve ?? exeContext.fieldResolver;
645647

646648
const info = buildResolveInfo(

src/language/ast.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,7 @@ export interface FieldNode {
267267
readonly arguments?: ReadonlyArray<ArgumentNode>;
268268
readonly directives?: ReadonlyArray<DirectiveNode>;
269269
readonly selectionSet?: SelectionSetNode;
270+
readonly required?: Boolean;
270271
}
271272

272273
export interface ArgumentNode {

src/language/parser.js

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -225,9 +225,10 @@ export class Parser {
225225
if (this.peek(TokenKind.NAME)) {
226226
switch (this._lexer.token.value) {
227227
case 'query':
228+
return this.parseOperationDefinition(true);
228229
case 'mutation':
229230
case 'subscription':
230-
return this.parseOperationDefinition();
231+
return this.parseOperationDefinition(false);
231232
case 'fragment':
232233
return this.parseFragmentDefinition();
233234
case 'schema':
@@ -243,7 +244,7 @@ export class Parser {
243244
return this.parseTypeSystemExtension();
244245
}
245246
} else if (this.peek(TokenKind.BRACE_L)) {
246-
return this.parseOperationDefinition();
247+
return this.parseOperationDefinition(true);
247248
} else if (this.peekDescription()) {
248249
return this.parseTypeSystemDefinition();
249250
}
@@ -258,7 +259,7 @@ export class Parser {
258259
* - SelectionSet
259260
* - OperationType Name? VariableDefinitions? Directives? SelectionSet
260261
*/
261-
parseOperationDefinition(): OperationDefinitionNode {
262+
parseOperationDefinition(allowRequiredField: boolean): OperationDefinitionNode {
262263
const start = this._lexer.token;
263264
if (this.peek(TokenKind.BRACE_L)) {
264265
return {
@@ -267,7 +268,7 @@ export class Parser {
267268
name: undefined,
268269
variableDefinitions: [],
269270
directives: [],
270-
selectionSet: this.parseSelectionSet(),
271+
selectionSet: this.parseSelectionSet(allowRequiredField),
271272
loc: this.loc(start),
272273
};
273274
}
@@ -282,7 +283,7 @@ export class Parser {
282283
name,
283284
variableDefinitions: this.parseVariableDefinitions(),
284285
directives: this.parseDirectives(false),
285-
selectionSet: this.parseSelectionSet(),
286+
selectionSet: this.parseSelectionSet(allowRequiredField),
286287
loc: this.loc(start),
287288
};
288289
}
@@ -348,13 +349,18 @@ export class Parser {
348349
/**
349350
* SelectionSet : { Selection+ }
350351
*/
351-
parseSelectionSet(): SelectionSetNode {
352+
parseSelectionSet(allowRequiredField: boolean = false): SelectionSetNode {
352353
const start = this._lexer.token;
354+
const _allowRequiredField = allowRequiredField
355+
let parseSelectionFn: () => SelectionNode = function (): SelectionNode {
356+
return this.parseSelection(_allowRequiredField)
357+
}
358+
353359
return {
354360
kind: Kind.SELECTION_SET,
355361
selections: this.many(
356362
TokenKind.BRACE_L,
357-
this.parseSelection,
363+
parseSelectionFn,
358364
TokenKind.BRACE_R,
359365
),
360366
loc: this.loc(start),
@@ -367,23 +373,30 @@ export class Parser {
367373
* - FragmentSpread
368374
* - InlineFragment
369375
*/
370-
parseSelection(): SelectionNode {
376+
parseSelection(allowRequiredField: boolean): SelectionNode {
371377
return this.peek(TokenKind.SPREAD)
372378
? this.parseFragment()
373-
: this.parseField();
379+
: this.parseField(allowRequiredField);
374380
}
375381

376382
/**
377383
* Field : Alias? Name Arguments? Directives? SelectionSet?
378384
*
379385
* Alias : Name :
380386
*/
381-
parseField(): FieldNode {
387+
parseField(allowRequiredField: boolean): FieldNode {
382388
const start = this._lexer.token;
383389

384390
const nameOrAlias = this.parseName();
385391
let alias;
386392
let name;
393+
let required;
394+
if (allowRequiredField && this.expectOptionalToken(TokenKind.BANG)) {
395+
required = true
396+
} else {
397+
required = false
398+
}
399+
387400
if (this.expectOptionalToken(TokenKind.COLON)) {
388401
alias = nameOrAlias;
389402
name = this.parseName();
@@ -398,8 +411,9 @@ export class Parser {
398411
arguments: this.parseArguments(false),
399412
directives: this.parseDirectives(false),
400413
selectionSet: this.peek(TokenKind.BRACE_L)
401-
? this.parseSelectionSet()
414+
? this.parseSelectionSet(allowRequiredField)
402415
: undefined,
416+
required: required,
403417
loc: this.loc(start),
404418
};
405419
}
@@ -464,7 +478,7 @@ export class Parser {
464478
kind: Kind.INLINE_FRAGMENT,
465479
typeCondition: hasTypeCondition ? this.parseNamedType() : undefined,
466480
directives: this.parseDirectives(false),
467-
selectionSet: this.parseSelectionSet(),
481+
selectionSet: this.parseSelectionSet(true),
468482
loc: this.loc(start),
469483
};
470484
}
@@ -488,7 +502,7 @@ export class Parser {
488502
variableDefinitions: this.parseVariableDefinitions(),
489503
typeCondition: (this.expectKeyword('on'), this.parseNamedType()),
490504
directives: this.parseDirectives(false),
491-
selectionSet: this.parseSelectionSet(),
505+
selectionSet: this.parseSelectionSet(true),
492506
loc: this.loc(start),
493507
};
494508
}
@@ -497,7 +511,7 @@ export class Parser {
497511
name: this.parseFragmentName(),
498512
typeCondition: (this.expectKeyword('on'), this.parseNamedType()),
499513
directives: this.parseDirectives(false),
500-
selectionSet: this.parseSelectionSet(),
514+
selectionSet: this.parseSelectionSet(true),
501515
loc: this.loc(start),
502516
};
503517
}

src/language/printer.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,8 @@ const printDocASTReducer: any = {
4343
wrap(' ', join(directives, ' ')),
4444
SelectionSet: ({ selections }) => block(selections),
4545

46-
Field: ({ alias, name, arguments: args, directives, selectionSet }) => {
47-
const prefix = wrap('', alias, ': ') + name;
46+
Field: ({ alias, name, arguments: args, directives, selectionSet, required }) => {
47+
const prefix = wrap('', alias, ': ') + name + (required ? '!' : '');
4848
let argsLine = prefix + wrap('(', join(args, ', '), ')');
4949

5050
if (argsLine.length > MAX_LINE_LENGTH) {
@@ -56,6 +56,7 @@ const printDocASTReducer: any = {
5656

5757
Argument: ({ name, value }) => name + ': ' + value,
5858

59+
5960
// Fragments
6061

6162
FragmentSpread: ({ name, directives }) =>

0 commit comments

Comments
 (0)