Skip to content

Commit b80ff6d

Browse files
committed
bring back valueFromAST but deprecate it
1 parent ec0ca36 commit b80ff6d

File tree

4 files changed

+473
-0
lines changed

4 files changed

+473
-0
lines changed

src/index.ts

+3
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,9 @@ export {
439439
printIntrospectionSchema,
440440
// Create a GraphQLType from a GraphQL language AST.
441441
typeFromAST,
442+
// Create a JavaScript value from a GraphQL language AST with a Type.
443+
/** @deprecated use `coerceInputLiteral()` instead - will be removed in v18 */
444+
valueFromAST,
442445
// Create a JavaScript value from a GraphQL language AST without a Type.
443446
valueFromASTUntyped,
444447
// Create a GraphQL language AST from a JavaScript value.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,289 @@
1+
import { assert, expect } from 'chai';
2+
import { describe, it } from 'mocha';
3+
4+
import { identityFunc } from '../../jsutils/identityFunc.js';
5+
import type { ObjMap } from '../../jsutils/ObjMap.js';
6+
7+
import { parseValue } from '../../language/parser.js';
8+
9+
import type { GraphQLInputType } from '../../type/definition.js';
10+
import {
11+
GraphQLEnumType,
12+
GraphQLInputObjectType,
13+
GraphQLList,
14+
GraphQLNonNull,
15+
GraphQLScalarType,
16+
} from '../../type/definition.js';
17+
import {
18+
GraphQLBoolean,
19+
GraphQLFloat,
20+
GraphQLID,
21+
GraphQLInt,
22+
GraphQLString,
23+
} from '../../type/scalars.js';
24+
25+
import { valueFromAST } from '../valueFromAST.js';
26+
27+
/** @deprecated use `coerceInputLiteral()` instead - will be removed in v18 */
28+
describe('valueFromAST', () => {
29+
function expectValueFrom(
30+
valueText: string,
31+
type: GraphQLInputType,
32+
variables?: ObjMap<unknown>,
33+
) {
34+
const ast = parseValue(valueText);
35+
const value = valueFromAST(ast, type, variables);
36+
return expect(value);
37+
}
38+
39+
it('rejects empty input', () => {
40+
expect(valueFromAST(null, GraphQLBoolean)).to.deep.equal(undefined);
41+
});
42+
43+
it('converts according to input coercion rules', () => {
44+
expectValueFrom('true', GraphQLBoolean).to.equal(true);
45+
expectValueFrom('false', GraphQLBoolean).to.equal(false);
46+
expectValueFrom('123', GraphQLInt).to.equal(123);
47+
expectValueFrom('123', GraphQLFloat).to.equal(123);
48+
expectValueFrom('123.456', GraphQLFloat).to.equal(123.456);
49+
expectValueFrom('"abc123"', GraphQLString).to.equal('abc123');
50+
expectValueFrom('123456', GraphQLID).to.equal('123456');
51+
expectValueFrom('"123456"', GraphQLID).to.equal('123456');
52+
});
53+
54+
it('does not convert when input coercion rules reject a value', () => {
55+
expectValueFrom('123', GraphQLBoolean).to.equal(undefined);
56+
expectValueFrom('123.456', GraphQLInt).to.equal(undefined);
57+
expectValueFrom('true', GraphQLInt).to.equal(undefined);
58+
expectValueFrom('"123"', GraphQLInt).to.equal(undefined);
59+
expectValueFrom('"123"', GraphQLFloat).to.equal(undefined);
60+
expectValueFrom('123', GraphQLString).to.equal(undefined);
61+
expectValueFrom('true', GraphQLString).to.equal(undefined);
62+
expectValueFrom('123.456', GraphQLString).to.equal(undefined);
63+
});
64+
65+
it('convert using parseLiteral from a custom scalar type', () => {
66+
const passthroughScalar = new GraphQLScalarType({
67+
name: 'PassthroughScalar',
68+
parseLiteral(node) {
69+
assert(node.kind === 'StringValue');
70+
return node.value;
71+
},
72+
parseValue: identityFunc,
73+
});
74+
75+
expectValueFrom('"value"', passthroughScalar).to.equal('value');
76+
77+
const throwScalar = new GraphQLScalarType({
78+
name: 'ThrowScalar',
79+
parseLiteral() {
80+
throw new Error('Test');
81+
},
82+
parseValue: identityFunc,
83+
});
84+
85+
expectValueFrom('value', throwScalar).to.equal(undefined);
86+
87+
const returnUndefinedScalar = new GraphQLScalarType({
88+
name: 'ReturnUndefinedScalar',
89+
parseLiteral() {
90+
return undefined;
91+
},
92+
parseValue: identityFunc,
93+
});
94+
95+
expectValueFrom('value', returnUndefinedScalar).to.equal(undefined);
96+
});
97+
98+
it('converts enum values according to input coercion rules', () => {
99+
const testEnum = new GraphQLEnumType({
100+
name: 'TestColor',
101+
values: {
102+
RED: { value: 1 },
103+
GREEN: { value: 2 },
104+
BLUE: { value: 3 },
105+
NULL: { value: null },
106+
NAN: { value: NaN },
107+
NO_CUSTOM_VALUE: { value: undefined },
108+
},
109+
});
110+
111+
expectValueFrom('RED', testEnum).to.equal(1);
112+
expectValueFrom('BLUE', testEnum).to.equal(3);
113+
expectValueFrom('3', testEnum).to.equal(undefined);
114+
expectValueFrom('"BLUE"', testEnum).to.equal(undefined);
115+
expectValueFrom('null', testEnum).to.equal(null);
116+
expectValueFrom('NULL', testEnum).to.equal(null);
117+
expectValueFrom('NULL', new GraphQLNonNull(testEnum)).to.equal(null);
118+
expectValueFrom('NAN', testEnum).to.deep.equal(NaN);
119+
expectValueFrom('NO_CUSTOM_VALUE', testEnum).to.equal('NO_CUSTOM_VALUE');
120+
});
121+
122+
// Boolean!
123+
const nonNullBool = new GraphQLNonNull(GraphQLBoolean);
124+
// [Boolean]
125+
const listOfBool = new GraphQLList(GraphQLBoolean);
126+
// [Boolean!]
127+
const listOfNonNullBool = new GraphQLList(nonNullBool);
128+
// [Boolean]!
129+
const nonNullListOfBool = new GraphQLNonNull(listOfBool);
130+
// [Boolean!]!
131+
const nonNullListOfNonNullBool = new GraphQLNonNull(listOfNonNullBool);
132+
133+
it('coerces to null unless non-null', () => {
134+
expectValueFrom('null', GraphQLBoolean).to.equal(null);
135+
expectValueFrom('null', nonNullBool).to.equal(undefined);
136+
});
137+
138+
it('coerces lists of values', () => {
139+
expectValueFrom('true', listOfBool).to.deep.equal([true]);
140+
expectValueFrom('123', listOfBool).to.equal(undefined);
141+
expectValueFrom('null', listOfBool).to.equal(null);
142+
expectValueFrom('[true, false]', listOfBool).to.deep.equal([true, false]);
143+
expectValueFrom('[true, 123]', listOfBool).to.equal(undefined);
144+
expectValueFrom('[true, null]', listOfBool).to.deep.equal([true, null]);
145+
expectValueFrom('{ true: true }', listOfBool).to.equal(undefined);
146+
});
147+
148+
it('coerces non-null lists of values', () => {
149+
expectValueFrom('true', nonNullListOfBool).to.deep.equal([true]);
150+
expectValueFrom('123', nonNullListOfBool).to.equal(undefined);
151+
expectValueFrom('null', nonNullListOfBool).to.equal(undefined);
152+
expectValueFrom('[true, false]', nonNullListOfBool).to.deep.equal([
153+
true,
154+
false,
155+
]);
156+
expectValueFrom('[true, 123]', nonNullListOfBool).to.equal(undefined);
157+
expectValueFrom('[true, null]', nonNullListOfBool).to.deep.equal([
158+
true,
159+
null,
160+
]);
161+
});
162+
163+
it('coerces lists of non-null values', () => {
164+
expectValueFrom('true', listOfNonNullBool).to.deep.equal([true]);
165+
expectValueFrom('123', listOfNonNullBool).to.equal(undefined);
166+
expectValueFrom('null', listOfNonNullBool).to.equal(null);
167+
expectValueFrom('[true, false]', listOfNonNullBool).to.deep.equal([
168+
true,
169+
false,
170+
]);
171+
expectValueFrom('[true, 123]', listOfNonNullBool).to.equal(undefined);
172+
expectValueFrom('[true, null]', listOfNonNullBool).to.equal(undefined);
173+
});
174+
175+
it('coerces non-null lists of non-null values', () => {
176+
expectValueFrom('true', nonNullListOfNonNullBool).to.deep.equal([true]);
177+
expectValueFrom('123', nonNullListOfNonNullBool).to.equal(undefined);
178+
expectValueFrom('null', nonNullListOfNonNullBool).to.equal(undefined);
179+
expectValueFrom('[true, false]', nonNullListOfNonNullBool).to.deep.equal([
180+
true,
181+
false,
182+
]);
183+
expectValueFrom('[true, 123]', nonNullListOfNonNullBool).to.equal(
184+
undefined,
185+
);
186+
expectValueFrom('[true, null]', nonNullListOfNonNullBool).to.equal(
187+
undefined,
188+
);
189+
});
190+
191+
const testInputObj = new GraphQLInputObjectType({
192+
name: 'TestInput',
193+
fields: {
194+
int: { type: GraphQLInt, defaultValue: 42 },
195+
bool: { type: GraphQLBoolean },
196+
requiredBool: { type: nonNullBool },
197+
},
198+
});
199+
const testOneOfInputObj = new GraphQLInputObjectType({
200+
name: 'TestOneOfInput',
201+
fields: {
202+
a: { type: GraphQLString },
203+
b: { type: GraphQLString },
204+
},
205+
isOneOf: true,
206+
});
207+
208+
it('coerces input objects according to input coercion rules', () => {
209+
expectValueFrom('null', testInputObj).to.equal(null);
210+
expectValueFrom('123', testInputObj).to.equal(undefined);
211+
expectValueFrom('[]', testInputObj).to.equal(undefined);
212+
expectValueFrom(
213+
'{ int: 123, requiredBool: false }',
214+
testInputObj,
215+
).to.deep.equal({
216+
int: 123,
217+
requiredBool: false,
218+
});
219+
expectValueFrom(
220+
'{ bool: true, requiredBool: false }',
221+
testInputObj,
222+
).to.deep.equal({
223+
int: 42,
224+
bool: true,
225+
requiredBool: false,
226+
});
227+
expectValueFrom('{ int: true, requiredBool: true }', testInputObj).to.equal(
228+
undefined,
229+
);
230+
expectValueFrom('{ requiredBool: null }', testInputObj).to.equal(undefined);
231+
expectValueFrom('{ bool: true }', testInputObj).to.equal(undefined);
232+
expectValueFrom('{ a: "abc" }', testOneOfInputObj).to.deep.equal({
233+
a: 'abc',
234+
});
235+
expectValueFrom('{ b: "def" }', testOneOfInputObj).to.deep.equal({
236+
b: 'def',
237+
});
238+
expectValueFrom('{ a: "abc", b: null }', testOneOfInputObj).to.deep.equal(
239+
undefined,
240+
);
241+
expectValueFrom('{ a: null }', testOneOfInputObj).to.equal(undefined);
242+
expectValueFrom('{ a: 1 }', testOneOfInputObj).to.equal(undefined);
243+
expectValueFrom('{ a: "abc", b: "def" }', testOneOfInputObj).to.equal(
244+
undefined,
245+
);
246+
expectValueFrom('{}', testOneOfInputObj).to.equal(undefined);
247+
expectValueFrom('{ c: "abc" }', testOneOfInputObj).to.equal(undefined);
248+
});
249+
250+
it('accepts variable values assuming already coerced', () => {
251+
expectValueFrom('$var', GraphQLBoolean, {}).to.equal(undefined);
252+
expectValueFrom('$var', GraphQLBoolean, { var: true }).to.equal(true);
253+
expectValueFrom('$var', GraphQLBoolean, { var: null }).to.equal(null);
254+
expectValueFrom('$var', nonNullBool, { var: null }).to.equal(undefined);
255+
});
256+
257+
it('asserts variables are provided as items in lists', () => {
258+
expectValueFrom('[ $foo ]', listOfBool, {}).to.deep.equal([null]);
259+
expectValueFrom('[ $foo ]', listOfNonNullBool, {}).to.equal(undefined);
260+
expectValueFrom('[ $foo ]', listOfNonNullBool, {
261+
foo: true,
262+
}).to.deep.equal([true]);
263+
// Note: variables are expected to have already been coerced, so we
264+
// do not expect the singleton wrapping behavior for variables.
265+
expectValueFrom('$foo', listOfNonNullBool, { foo: true }).to.equal(true);
266+
expectValueFrom('$foo', listOfNonNullBool, { foo: [true] }).to.deep.equal([
267+
true,
268+
]);
269+
});
270+
271+
it('omits input object fields for unprovided variables', () => {
272+
expectValueFrom(
273+
'{ int: $foo, bool: $foo, requiredBool: true }',
274+
testInputObj,
275+
{},
276+
).to.deep.equal({ int: 42, requiredBool: true });
277+
278+
expectValueFrom('{ requiredBool: $foo }', testInputObj, {}).to.equal(
279+
undefined,
280+
);
281+
282+
expectValueFrom('{ requiredBool: $foo }', testInputObj, {
283+
foo: true,
284+
}).to.deep.equal({
285+
int: 42,
286+
requiredBool: true,
287+
});
288+
});
289+
});

src/utilities/index.ts

+6
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,12 @@ export {
5656
// Create a GraphQLType from a GraphQL language AST.
5757
export { typeFromAST } from './typeFromAST.js';
5858

59+
// Create a JavaScript value from a GraphQL language AST with a type.
60+
export {
61+
/** @deprecated use `coerceInputLiteral()` instead - will be removed in v18 */
62+
valueFromAST,
63+
} from './valueFromAST.js';
64+
5965
// Create a JavaScript value from a GraphQL language AST without a type.
6066
export { valueFromASTUntyped } from './valueFromASTUntyped.js';
6167

0 commit comments

Comments
 (0)