Skip to content

Commit 0afeed9

Browse files
committed
Ensure isValidValue and isValidLiteralValue share same logic and cover same cases. add tests
1 parent a973b4d commit 0afeed9

File tree

5 files changed

+93
-40
lines changed

5 files changed

+93
-40
lines changed

src/executor/__tests__/variables.js

Lines changed: 40 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,25 @@ describe('Execute: Handles inputs', () => {
193193
locations: [ { line: 2, column: 17 } ],
194194
message:
195195
'Variable $input expected value of type TestInputObject but ' +
196-
'got: {\"a\":\"foo\",\"b\":\"bar\",\"c\":null}.'
196+
'got: {"a":"foo","b":"bar","c":null}.'
197+
});
198+
});
199+
200+
it('errors on incorrect type', async () => {
201+
var params = {input: 'foo bar'};
202+
203+
var caughtError;
204+
try {
205+
execute(schema, null, ast, null, params);
206+
} catch (error) {
207+
caughtError = error;
208+
}
209+
210+
expect(caughtError).to.containSubset({
211+
locations: [ { line: 2, column: 17 } ],
212+
message:
213+
'Variable $input expected value of type TestInputObject but ' +
214+
'got: "foo bar".'
197215
});
198216
});
199217

@@ -211,7 +229,25 @@ describe('Execute: Handles inputs', () => {
211229
locations: [ { line: 2, column: 17 } ],
212230
message:
213231
'Variable $input expected value of type TestInputObject but ' +
214-
'got: {\"a\":\"foo\",\"b\":\"bar\"}.'
232+
'got: {"a":"foo","b":"bar"}.'
233+
});
234+
});
235+
236+
it('errors on addition of unknown input field', async () => {
237+
var params = {input: {a: 'foo', b: 'bar', c: 'baz', d: 'dog'}};
238+
239+
var caughtError;
240+
try {
241+
execute(schema, null, ast, null, params);
242+
} catch (error) {
243+
caughtError = error;
244+
}
245+
246+
expect(caughtError).to.containSubset({
247+
locations: [ { line: 2, column: 17 } ],
248+
message:
249+
'Variable $input expected value of type TestInputObject but ' +
250+
'got: {"a":"foo","b":"bar","c":"baz","d":"dog"}.'
215251
});
216252
});
217253

@@ -568,7 +604,7 @@ describe('Execute: Handles inputs', () => {
568604
locations: [ { line: 2, column: 17 } ],
569605
message:
570606
'Variable $input expected value of type [String!] but got: ' +
571-
'[\"A\",null,\"B\"].'
607+
'["A",null,"B"].'
572608
});
573609
});
574610

@@ -631,7 +667,7 @@ describe('Execute: Handles inputs', () => {
631667
locations: [ { line: 2, column: 17 } ],
632668
message:
633669
'Variable $input expected value of type [String!]! but got: ' +
634-
'[\"A\",null,\"B\"].'
670+
'["A",null,"B"].'
635671
});
636672
});
637673

src/executor/values.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ function getVariableValue(
109109
* Given a type and any value, return true if that value is valid.
110110
*/
111111
function isValidValue(type: GraphQLInputType, value: any): boolean {
112+
// A value must be provided if the type is non-null.
112113
if (type instanceof GraphQLNonNull) {
113114
if (isNullish(value)) {
114115
return false;
@@ -120,6 +121,7 @@ function isValidValue(type: GraphQLInputType, value: any): boolean {
120121
return true;
121122
}
122123

124+
// Lists accept a non-list value as a list of one.
123125
if (type instanceof GraphQLList) {
124126
var itemType = type.ofType;
125127
if (Array.isArray(value)) {
@@ -129,8 +131,19 @@ function isValidValue(type: GraphQLInputType, value: any): boolean {
129131
}
130132
}
131133

134+
// Input objects check each defined field.
132135
if (type instanceof GraphQLInputObjectType) {
136+
if (typeof value !== 'object') {
137+
return false;
138+
}
133139
var fields = type.getFields();
140+
141+
// Ensure every provided field is defined.
142+
if (Object.keys(value).some(fieldName => !fields[fieldName])) {
143+
return false;
144+
}
145+
146+
// Ensure every defined field is valid.
134147
return Object.keys(fields).every(
135148
fieldName => isValidValue(fields[fieldName].type, value[fieldName])
136149
);
@@ -141,6 +154,8 @@ function isValidValue(type: GraphQLInputType, value: any): boolean {
141154
'Must be input type'
142155
);
143156

157+
// Scalar/Enum input checks to ensure the type can coerce the value to
158+
// a non-null value.
144159
return !isNullish(type.coerce(value));
145160
}
146161

src/utils/isValidLiteralValue.js

Lines changed: 36 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import {
2121
GraphQLList,
2222
GraphQLNonNull
2323
} from '../type/definition';
24-
import type { GraphQLType } from '../type/definition';
24+
import type { GraphQLInputType } from '../type/definition';
25+
import invariant from './invariant';
2526
import keyMap from './keyMap';
2627
import isNullish from './isNullish';
2728

@@ -34,17 +35,20 @@ import isNullish from './isNullish';
3435
* provide values of the correct type.
3536
*/
3637
export default function isValidLiteralValue(
37-
valueAST: Value,
38-
type: GraphQLType
38+
type: GraphQLInputType,
39+
valueAST: Value
3940
): boolean {
40-
// A value can only be not provided if the type is nullable.
41-
if (!valueAST) {
42-
return !(type instanceof GraphQLNonNull);
41+
// A value must be provided if the type is non-null.
42+
if (type instanceof GraphQLNonNull) {
43+
if (!valueAST) {
44+
return false;
45+
}
46+
var ofType: GraphQLInputType = (type.ofType: any);
47+
return isValidLiteralValue(ofType, valueAST);
4348
}
4449

45-
// Unwrap non-null.
46-
if (type instanceof GraphQLNonNull) {
47-
return isValidLiteralValue(valueAST, type.ofType);
50+
if (!valueAST) {
51+
return true;
4852
}
4953

5054
// This function only tests literals, and assumes variables will provide
@@ -55,45 +59,43 @@ export default function isValidLiteralValue(
5559

5660
// Lists accept a non-list value as a list of one.
5761
if (type instanceof GraphQLList) {
58-
var itemType = type.ofType;
62+
var itemType: GraphQLInputType = (type.ofType: any);
5963
if (valueAST.kind === ARRAY) {
6064
return (valueAST: ArrayValue).values.every(
61-
itemAST => isValidLiteralValue(itemAST, itemType)
65+
itemAST => isValidLiteralValue(itemType, itemAST)
6266
);
6367
} else {
64-
return isValidLiteralValue(valueAST, itemType);
68+
return isValidLiteralValue(itemType, valueAST);
6569
}
6670
}
6771

68-
// Scalar/Enum input checks to ensure the type can coerce the value to
69-
// a non-null value.
70-
if (type instanceof GraphQLScalarType ||
71-
type instanceof GraphQLEnumType) {
72-
return !isNullish(type.coerceLiteral(valueAST));
73-
}
74-
75-
// Input objects check each defined field, ensuring it is of the correct
76-
// type and provided if non-nullable.
72+
// Input objects check each defined field and look for undefined fields.
7773
if (type instanceof GraphQLInputObjectType) {
78-
var fields = type.getFields();
7974
if (valueAST.kind !== OBJECT) {
8075
return false;
8176
}
77+
var fields = type.getFields();
78+
79+
// Ensure every provided field is defined.
8280
var fieldASTs = (valueAST: ObjectValue).fields;
83-
var fieldASTMap = keyMap(fieldASTs, field => field.name.value);
84-
var isMissingFields = Object.keys(fields).some(fieldName =>
85-
!fieldASTMap[fieldName] &&
86-
fields[fieldName].type instanceof GraphQLNonNull
87-
);
88-
if (isMissingFields) {
81+
if (fieldASTs.some(fieldAST => !fields[fieldAST.name.value])) {
8982
return false;
9083
}
91-
return fieldASTs.every(fieldAST =>
92-
fields[fieldAST.name.value] &&
93-
isValidLiteralValue(fieldAST.value, fields[fieldAST.name.value].type)
94-
);
84+
85+
// Ensure every defined field is valid.
86+
var fieldASTMap = keyMap(fieldASTs, fieldAST => fieldAST.name.value);
87+
return Object.keys(fields).every(fieldName => isValidLiteralValue(
88+
fields[fieldName].type,
89+
fieldASTMap[fieldName] && fieldASTMap[fieldName].value
90+
));
9591
}
9692

97-
// Any other kind of type is not an input type, and a literal cannot be used.
98-
return false;
93+
invariant(
94+
type instanceof GraphQLScalarType || type instanceof GraphQLEnumType,
95+
'Must be input type'
96+
);
97+
98+
// Scalar/Enum input checks to ensure the type can coerce the value to
99+
// a non-null value.
100+
return !isNullish(type.coerceLiteral(valueAST));
99101
}

src/validator/rules/ArgumentsOfCorrectType.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ export default function ArgumentsOfCorrectType(
2828
return {
2929
Argument(argAST) {
3030
var argDef = context.getArgument();
31-
if (argDef && !isValidLiteralValue(argAST.value, argDef.type)) {
31+
if (argDef && !isValidLiteralValue(argDef.type, argAST.value)) {
3232
return new GraphQLError(
3333
badValueMessage(
3434
argAST.name.value,

src/validator/rules/DefaultValuesOfCorrectType.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ export default function DefaultValuesOfCorrectType(
4040
[defaultValue]
4141
);
4242
}
43-
if (type && defaultValue && !isValidLiteralValue(defaultValue, type)) {
43+
if (type && defaultValue && !isValidLiteralValue(type, defaultValue)) {
4444
return new GraphQLError(
4545
badValueForDefaultArgMessage(name, type, print(defaultValue)),
4646
[defaultValue]

0 commit comments

Comments
 (0)