Skip to content

Commit d019e2e

Browse files
committed
Revert change requiring config flag & more specific allowedInPosition definition
Based on discussion with @dschafer Adds getDefaultValue() to TypeInfo so the default value at any position in an AST visit is known.
1 parent 246b998 commit d019e2e

10 files changed

+96
-128
lines changed

src/graphql.js

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@
99

1010
import { validateSchema } from './type/validate';
1111
import { parse } from './language/parser';
12-
import { validate, specifiedRules } from './validation';
12+
import { validate } from './validation/validate';
1313
import { execute } from './execution/execute';
1414
import type { ObjMap } from './jsutils/ObjMap';
15-
import type { ParseOptions, Source } from './language';
15+
import type { Source } from './language/source';
1616
import type { GraphQLFieldResolver } from './type/definition';
1717
import type { GraphQLSchema } from './type/schema';
1818
import type { ExecutionResult } from './execution/execute';
19-
import type { ValidationOptions } from './validation';
2019
import type { MaybePromise } from './jsutils/MaybePromise';
2120

2221
/**
@@ -48,9 +47,6 @@ import type { MaybePromise } from './jsutils/MaybePromise';
4847
* A resolver function to use when one is not provided by the schema.
4948
* If not provided, the default field resolver is used (which looks for a
5049
* value or method on the source value with the field's name).
51-
* options:
52-
* An additional set of flags which enable legacy, compataibility, or
53-
* experimental behavior.
5450
*/
5551
export type GraphQLArgs = {|
5652
schema: GraphQLSchema,
@@ -60,7 +56,6 @@ export type GraphQLArgs = {|
6056
variableValues?: ?ObjMap<mixed>,
6157
operationName?: ?string,
6258
fieldResolver?: ?GraphQLFieldResolver<any, any>,
63-
options?: ParseOptions & ValidationOptions,
6459
|};
6560
declare function graphql(GraphQLArgs, ..._: []): Promise<ExecutionResult>;
6661
/* eslint-disable no-redeclare */
@@ -72,7 +67,6 @@ declare function graphql(
7267
variableValues?: ?ObjMap<mixed>,
7368
operationName?: ?string,
7469
fieldResolver?: ?GraphQLFieldResolver<any, any>,
75-
options?: ParseOptions & ValidationOptions,
7670
): Promise<ExecutionResult>;
7771
export function graphql(
7872
argsOrSchema,
@@ -82,7 +76,6 @@ export function graphql(
8276
variableValues,
8377
operationName,
8478
fieldResolver,
85-
options,
8679
) {
8780
/* eslint-enable no-redeclare */
8881
// Always return a Promise for a consistent API.
@@ -98,7 +91,6 @@ export function graphql(
9891
argsOrSchema.variableValues,
9992
argsOrSchema.operationName,
10093
argsOrSchema.fieldResolver,
101-
argsOrSchema.options,
10294
)
10395
: graphqlImpl(
10496
argsOrSchema,
@@ -108,7 +100,6 @@ export function graphql(
108100
variableValues,
109101
operationName,
110102
fieldResolver,
111-
options,
112103
),
113104
),
114105
);
@@ -130,7 +121,6 @@ declare function graphqlSync(
130121
variableValues?: ?ObjMap<mixed>,
131122
operationName?: ?string,
132123
fieldResolver?: ?GraphQLFieldResolver<any, any>,
133-
options?: ParseOptions & ValidationOptions,
134124
): ExecutionResult;
135125
export function graphqlSync(
136126
argsOrSchema,
@@ -140,7 +130,6 @@ export function graphqlSync(
140130
variableValues,
141131
operationName,
142132
fieldResolver,
143-
options,
144133
) {
145134
// Extract arguments from object args if provided.
146135
const result =
@@ -153,7 +142,6 @@ export function graphqlSync(
153142
argsOrSchema.variableValues,
154143
argsOrSchema.operationName,
155144
argsOrSchema.fieldResolver,
156-
argsOrSchema.options,
157145
)
158146
: graphqlImpl(
159147
argsOrSchema,
@@ -163,7 +151,6 @@ export function graphqlSync(
163151
variableValues,
164152
operationName,
165153
fieldResolver,
166-
options,
167154
);
168155

169156
// Assert that the execution was synchronous.
@@ -182,7 +169,6 @@ function graphqlImpl(
182169
variableValues,
183170
operationName,
184171
fieldResolver,
185-
options,
186172
): MaybePromise<ExecutionResult> {
187173
// Validate Schema
188174
const schemaValidationErrors = validateSchema(schema);
@@ -193,13 +179,13 @@ function graphqlImpl(
193179
// Parse
194180
let document;
195181
try {
196-
document = parse(source, options);
182+
document = parse(source);
197183
} catch (syntaxError) {
198184
return { errors: [syntaxError] };
199185
}
200186

201187
// Validate
202-
const validationErrors = validate(schema, document, specifiedRules, options);
188+
const validationErrors = validate(schema, document);
203189
if (validationErrors.length > 0) {
204190
return { errors: validationErrors };
205191
}

src/index.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -268,7 +268,6 @@ export { subscribe, createSourceEventStream } from './subscription';
268268
// Validate GraphQL queries.
269269
export {
270270
validate,
271-
ValidationOptions,
272271
ValidationContext,
273272
// All validation rules in the GraphQL Specification.
274273
specifiedRules,

src/utilities/TypeInfo.js

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import type {
2727
GraphQLCompositeType,
2828
GraphQLField,
2929
GraphQLArgument,
30+
GraphQLInputField,
3031
GraphQLEnumValue,
3132
} from '../type/definition';
3233
import type { GraphQLDirective } from '../type/directives';
@@ -51,6 +52,7 @@ export class TypeInfo {
5152
_parentTypeStack: Array<?GraphQLCompositeType>;
5253
_inputTypeStack: Array<?GraphQLInputType>;
5354
_fieldDefStack: Array<?GraphQLField<*, *>>;
55+
_defaultValueStack: Array<?mixed>;
5456
_directive: ?GraphQLDirective;
5557
_argument: ?GraphQLArgument;
5658
_enumValue: ?GraphQLEnumValue;
@@ -71,6 +73,7 @@ export class TypeInfo {
7173
this._parentTypeStack = [];
7274
this._inputTypeStack = [];
7375
this._fieldDefStack = [];
76+
this._defaultValueStack = [];
7477
this._directive = null;
7578
this._argument = null;
7679
this._enumValue = null;
@@ -118,6 +121,12 @@ export class TypeInfo {
118121
}
119122
}
120123

124+
getDefaultValue(): ?mixed {
125+
if (this._defaultValueStack.length > 0) {
126+
return this._defaultValueStack[this._defaultValueStack.length - 1];
127+
}
128+
}
129+
121130
getDirective(): ?GraphQLDirective {
122131
return this._directive;
123132
}
@@ -199,24 +208,31 @@ export class TypeInfo {
199208
}
200209
}
201210
this._argument = argDef;
211+
this._defaultValueStack.push(argDef ? argDef.defaultValue : undefined);
202212
this._inputTypeStack.push(isInputType(argType) ? argType : undefined);
203213
break;
204214
case Kind.LIST:
205215
const listType: mixed = getNullableType(this.getInputType());
206216
const itemType: mixed = isListType(listType)
207217
? listType.ofType
208218
: listType;
219+
// List positions never have a default value.
220+
this._defaultValueStack.push(undefined);
209221
this._inputTypeStack.push(isInputType(itemType) ? itemType : undefined);
210222
break;
211223
case Kind.OBJECT_FIELD:
212224
const objectType: mixed = getNamedType(this.getInputType());
213-
let inputFieldType: mixed;
225+
let inputFieldType: GraphQLInputType | void;
226+
let inputField: GraphQLInputField | void;
214227
if (isInputObjectType(objectType)) {
215-
const inputField = objectType.getFields()[node.name.value];
228+
inputField = objectType.getFields()[node.name.value];
216229
if (inputField) {
217230
inputFieldType = inputField.type;
218231
}
219232
}
233+
this._defaultValueStack.push(
234+
inputField ? inputField.defaultValue : undefined,
235+
);
220236
this._inputTypeStack.push(
221237
isInputType(inputFieldType) ? inputFieldType : undefined,
222238
);
@@ -254,10 +270,12 @@ export class TypeInfo {
254270
break;
255271
case Kind.ARGUMENT:
256272
this._argument = null;
273+
this._defaultValueStack.pop();
257274
this._inputTypeStack.pop();
258275
break;
259276
case Kind.LIST:
260277
case Kind.OBJECT_FIELD:
278+
this._defaultValueStack.pop();
261279
this._inputTypeStack.pop();
262280
break;
263281
case Kind.ENUM:

src/validation/ValidationContext.js

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -31,22 +31,11 @@ import type { GraphQLDirective } from '../type/directives';
3131
import { TypeInfo } from '../utilities/TypeInfo';
3232

3333
type NodeWithSelectionSet = OperationDefinitionNode | FragmentDefinitionNode;
34-
type VariableUsage = { node: VariableNode, type: ?GraphQLInputType };
35-
36-
/**
37-
* Configuration options to control validation behavior.
38-
*/
39-
export type ValidationOptions = {
40-
/**
41-
* If enabled, validation will allow nullable variables with default values
42-
* to be used in non-null positions. Earlier versions explicitly allowed such
43-
* operations due to a slightly different interpretation of default values and
44-
* null values. GraphQL services accepting operations written before this
45-
* version may continue to allow such operations by enabling this option,
46-
* however GraphQL services established after this version should not.
47-
*/
48-
allowNullableVariablesInNonNullPositions?: boolean,
49-
};
34+
type VariableUsage = {|
35+
+node: VariableNode,
36+
+type: ?GraphQLInputType,
37+
+defaultValue: ?mixed,
38+
|};
5039

5140
/**
5241
* An instance of this class is passed as the "this" context to all validators,
@@ -57,7 +46,6 @@ export default class ValidationContext {
5746
_schema: GraphQLSchema;
5847
_ast: DocumentNode;
5948
_typeInfo: TypeInfo;
60-
options: ValidationOptions;
6149
_errors: Array<GraphQLError>;
6250
_fragments: ObjMap<FragmentDefinitionNode>;
6351
_fragmentSpreads: Map<SelectionSetNode, $ReadOnlyArray<FragmentSpreadNode>>;
@@ -75,12 +63,10 @@ export default class ValidationContext {
7563
schema: GraphQLSchema,
7664
ast: DocumentNode,
7765
typeInfo: TypeInfo,
78-
options?: ValidationOptions,
7966
): void {
8067
this._schema = schema;
8168
this._ast = ast;
8269
this._typeInfo = typeInfo;
83-
this.options = options || {};
8470
this._errors = [];
8571
this._fragmentSpreads = new Map();
8672
this._recursivelyReferencedFragments = new Map();
@@ -181,7 +167,11 @@ export default class ValidationContext {
181167
visitWithTypeInfo(typeInfo, {
182168
VariableDefinition: () => false,
183169
Variable(variable) {
184-
newUsages.push({ node: variable, type: typeInfo.getInputType() });
170+
newUsages.push({
171+
node: variable,
172+
type: typeInfo.getInputType(),
173+
defaultValue: typeInfo.getDefaultValue(),
174+
});
185175
},
186176
}),
187177
);

src/validation/__tests__/VariablesInAllowedPosition-test.js

Lines changed: 20 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,7 @@
66
*/
77

88
import { describe, it } from 'mocha';
9-
import {
10-
expectPassesRule,
11-
expectFailsRule,
12-
expectPassesRuleWithOptions,
13-
expectFailsRuleWithOptions,
14-
} from './harness';
9+
import { expectPassesRule, expectFailsRule } from './harness';
1510
import {
1611
VariablesInAllowedPosition,
1712
badVarPosMessage,
@@ -353,71 +348,56 @@ describe('Validate: Variables are in allowed positions', () => {
353348
);
354349
});
355350

356-
describe('allowNullableVariablesInNonNullPositions', () => {
357-
it('Int => Int! with non-null default value without option', () => {
351+
describe('Allows optional (nullable) variables with default values', () => {
352+
it('Int => Int! fails when variable provides null default value', () => {
358353
expectFailsRule(
359354
VariablesInAllowedPosition,
360355
`
361-
query Query($intVar: Int = 1) {
356+
query Query($intVar: Int = null) {
362357
complicatedArgs {
363358
nonNullIntArgField(nonNullIntArg: $intVar)
364359
}
365-
}
366-
`,
360+
}`,
367361
[
368362
{
369363
message: badVarPosMessage('intVar', 'Int', 'Int!'),
370364
locations: [{ line: 2, column: 21 }, { line: 4, column: 47 }],
371-
path: undefined,
372365
},
373366
],
374367
);
375368
});
376369

377-
it('Int => Int! with null default value fails with option', () => {
378-
expectFailsRuleWithOptions(
379-
{ allowNullableVariablesInNonNullPositions: true },
370+
it('Int => Int! when variable provides non-null default value', () => {
371+
expectPassesRule(
380372
VariablesInAllowedPosition,
381373
`
382-
query Query($intVar: Int = null) {
383-
complicatedArgs {
384-
nonNullIntArgField(nonNullIntArg: $intVar)
385-
}
374+
query Query($intVar: Int = 1) {
375+
complicatedArgs {
376+
nonNullIntArgField(nonNullIntArg: $intVar)
386377
}
387-
`,
388-
[
389-
{
390-
message: badVarPosMessage('intVar', 'Int', 'Int!'),
391-
locations: [{ line: 2, column: 23 }, { line: 4, column: 49 }],
392-
path: undefined,
393-
},
394-
],
378+
}`,
395379
);
396380
});
397381

398-
it('Int => Int! with non-null default value with option', () => {
399-
expectPassesRuleWithOptions(
400-
{ allowNullableVariablesInNonNullPositions: true },
382+
it('Int => Int! when optional argument provides default value', () => {
383+
expectPassesRule(
401384
VariablesInAllowedPosition,
402385
`
403-
query Query($intVar: Int = 1) {
386+
query Query($intVar: Int) {
404387
complicatedArgs {
405-
nonNullIntArgField(nonNullIntArg: $intVar)
388+
nonNullFieldWithDefault(nonNullIntArg: $intVar)
406389
}
407-
}
408-
`,
390+
}`,
409391
);
410392
});
411393

412394
it('Boolean => Boolean! in directive with default value with option', () => {
413-
expectPassesRuleWithOptions(
414-
{ allowNullableVariablesInNonNullPositions: true },
395+
expectPassesRule(
415396
VariablesInAllowedPosition,
416397
`
417-
query Query($boolVar: Boolean = false) {
418-
dog @include(if: $boolVar)
419-
}
420-
`,
398+
query Query($boolVar: Boolean = false) {
399+
dog @include(if: $boolVar)
400+
}`,
421401
);
422402
});
423403
});

0 commit comments

Comments
 (0)