Skip to content

Commit

Permalink
Added support for client defined required field
Browse files Browse the repository at this point in the history
  • Loading branch information
xuewei8910 committed May 19, 2021
1 parent 1611bbb commit a830bb1
Show file tree
Hide file tree
Showing 4 changed files with 35 additions and 17 deletions.
4 changes: 3 additions & 1 deletion src/execution/execute.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import type {
GraphQLResolveInfo,
GraphQLTypeResolver,
GraphQLList,
GraphQLNonNull,
} from '../type/definition';
import { assertValidSchema } from '../type/validate';
import {
Expand Down Expand Up @@ -640,7 +641,8 @@ function resolveField(
return;
}

const returnType = fieldDef.type;
const returnType = fieldNode.required ? new GraphQLNonNull(fieldDef.type) : fieldDef.type

const resolveFn = fieldDef.resolve ?? exeContext.fieldResolver;

const info = buildResolveInfo(
Expand Down
1 change: 1 addition & 0 deletions src/language/ast.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ export interface FieldNode {
readonly arguments?: ReadonlyArray<ArgumentNode>;
readonly directives?: ReadonlyArray<DirectiveNode>;
readonly selectionSet?: SelectionSetNode;
readonly required?: Boolean;
}

export interface ArgumentNode {
Expand Down
42 changes: 28 additions & 14 deletions src/language/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -225,9 +225,10 @@ export class Parser {
if (this.peek(TokenKind.NAME)) {
switch (this._lexer.token.value) {
case 'query':
return this.parseOperationDefinition(true);
case 'mutation':
case 'subscription':
return this.parseOperationDefinition();
return this.parseOperationDefinition(false);
case 'fragment':
return this.parseFragmentDefinition();
case 'schema':
Expand All @@ -243,7 +244,7 @@ export class Parser {
return this.parseTypeSystemExtension();
}
} else if (this.peek(TokenKind.BRACE_L)) {
return this.parseOperationDefinition();
return this.parseOperationDefinition(true);
} else if (this.peekDescription()) {
return this.parseTypeSystemDefinition();
}
Expand All @@ -258,7 +259,7 @@ export class Parser {
* - SelectionSet
* - OperationType Name? VariableDefinitions? Directives? SelectionSet
*/
parseOperationDefinition(): OperationDefinitionNode {
parseOperationDefinition(allowRequiredField: boolean): OperationDefinitionNode {
const start = this._lexer.token;
if (this.peek(TokenKind.BRACE_L)) {
return {
Expand All @@ -267,7 +268,7 @@ export class Parser {
name: undefined,
variableDefinitions: [],
directives: [],
selectionSet: this.parseSelectionSet(),
selectionSet: this.parseSelectionSet(allowRequiredField),
loc: this.loc(start),
};
}
Expand All @@ -282,7 +283,7 @@ export class Parser {
name,
variableDefinitions: this.parseVariableDefinitions(),
directives: this.parseDirectives(false),
selectionSet: this.parseSelectionSet(),
selectionSet: this.parseSelectionSet(allowRequiredField),
loc: this.loc(start),
};
}
Expand Down Expand Up @@ -348,13 +349,18 @@ export class Parser {
/**
* SelectionSet : { Selection+ }
*/
parseSelectionSet(): SelectionSetNode {
parseSelectionSet(allowRequiredField: boolean = false): SelectionSetNode {
const start = this._lexer.token;
const _allowRequiredField = allowRequiredField
let parseSelectionFn: () => SelectionNode = function (): SelectionNode {
return this.parseSelection(_allowRequiredField)
}

return {
kind: Kind.SELECTION_SET,
selections: this.many(
TokenKind.BRACE_L,
this.parseSelection,
parseSelectionFn,
TokenKind.BRACE_R,
),
loc: this.loc(start),
Expand All @@ -367,23 +373,30 @@ export class Parser {
* - FragmentSpread
* - InlineFragment
*/
parseSelection(): SelectionNode {
parseSelection(allowRequiredField: boolean): SelectionNode {
return this.peek(TokenKind.SPREAD)
? this.parseFragment()
: this.parseField();
: this.parseField(allowRequiredField);
}

/**
* Field : Alias? Name Arguments? Directives? SelectionSet?
*
* Alias : Name :
*/
parseField(): FieldNode {
parseField(allowRequiredField: boolean): FieldNode {
const start = this._lexer.token;

const nameOrAlias = this.parseName();
let alias;
let name;
let required;
if (allowRequiredField && this.expectOptionalToken(TokenKind.BANG)) {
required = true
} else {
required = false
}

if (this.expectOptionalToken(TokenKind.COLON)) {
alias = nameOrAlias;
name = this.parseName();
Expand All @@ -398,8 +411,9 @@ export class Parser {
arguments: this.parseArguments(false),
directives: this.parseDirectives(false),
selectionSet: this.peek(TokenKind.BRACE_L)
? this.parseSelectionSet()
? this.parseSelectionSet(allowRequiredField)
: undefined,
required: required,
loc: this.loc(start),
};
}
Expand Down Expand Up @@ -464,7 +478,7 @@ export class Parser {
kind: Kind.INLINE_FRAGMENT,
typeCondition: hasTypeCondition ? this.parseNamedType() : undefined,
directives: this.parseDirectives(false),
selectionSet: this.parseSelectionSet(),
selectionSet: this.parseSelectionSet(true),
loc: this.loc(start),
};
}
Expand All @@ -488,7 +502,7 @@ export class Parser {
variableDefinitions: this.parseVariableDefinitions(),
typeCondition: (this.expectKeyword('on'), this.parseNamedType()),
directives: this.parseDirectives(false),
selectionSet: this.parseSelectionSet(),
selectionSet: this.parseSelectionSet(true),
loc: this.loc(start),
};
}
Expand All @@ -497,7 +511,7 @@ export class Parser {
name: this.parseFragmentName(),
typeCondition: (this.expectKeyword('on'), this.parseNamedType()),
directives: this.parseDirectives(false),
selectionSet: this.parseSelectionSet(),
selectionSet: this.parseSelectionSet(true),
loc: this.loc(start),
};
}
Expand Down
5 changes: 3 additions & 2 deletions src/language/printer.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ const printDocASTReducer: any = {
wrap(' ', join(directives, ' ')),
SelectionSet: ({ selections }) => block(selections),

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

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

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


// Fragments

FragmentSpread: ({ name, directives }) =>
Expand Down

0 comments on commit a830bb1

Please sign in to comment.