Skip to content

Commit 8e57231

Browse files
leebyronyaacovCR
authored andcommitted
Add valueToLiteral()
* Adds `valueToLiteral()` which takes an external value and translates it to a literal, allowing for custom scalars to define this behavior. This also adds important changes to Input Coercion, especially for custom scalars: * Addition of `parseConstLiteral()` to leaf types which operates in parallel to `parseLiteral()` but take `ConstValueNode` instead of `ValueNode` -- the second `variables` argument has been removed. For all built-in scalars this has no effect, but any custom scalars which use complex literals no longer need to do variable reconciliation manually (in fact most do not -- this has been an easy subtle bug to miss). This behavior is possible with the addition of `replaceVariables()`. `parseLiteral()` is no longer used internally and has been marked for deprecation.
1 parent 55b9361 commit 8e57231

15 files changed

+920
-132
lines changed

integrationTests/ts/kitchenSink-test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ new GraphQLScalarType({
88
name: 'SomeScalar',
99
serialize: undefined,
1010
parseValue: undefined,
11-
parseLiteral: undefined,
11+
parseConstLiteral: undefined,
1212
});
1313

1414
new GraphQLError('test', { nodes: undefined });

src/execution/__tests__/variables-test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const TestFaultyScalar = new GraphQLScalarType({
4747
parseValue() {
4848
throw TestFaultyScalarGraphQLError;
4949
},
50-
parseLiteral() {
50+
parseConstLiteral() {
5151
throw TestFaultyScalarGraphQLError;
5252
},
5353
});
@@ -58,7 +58,7 @@ const TestComplexScalar = new GraphQLScalarType({
5858
expect(value).to.equal('SerializedValue');
5959
return 'DeserializedValue';
6060
},
61-
parseLiteral(ast) {
61+
parseConstLiteral(ast) {
6262
expect(ast).to.include({ kind: 'StringValue', value: 'SerializedValue' });
6363
return 'DeserializedValue';
6464
},
@@ -281,7 +281,7 @@ describe('Execute: Handles inputs', () => {
281281
});
282282
});
283283

284-
it('properly runs parseLiteral on complex scalar types', () => {
284+
it('properly runs parseConstLiteral on complex scalar types', () => {
285285
const result = executeQuery(`
286286
{
287287
fieldWithObjectInput(input: {c: "foo", d: "SerializedValue"})

src/index.ts

+4
Original file line numberDiff line numberDiff line change
@@ -450,6 +450,10 @@ export {
450450
// A helper to use within recursive-descent visitors which need to be aware of the GraphQL type system.
451451
TypeInfo,
452452
visitWithTypeInfo,
453+
// Converts a value to a const value by replacing variables.
454+
replaceVariables,
455+
// Create a GraphQL literal (AST) from a JavaScript input value.
456+
valueToLiteral,
453457
// Coerces a JavaScript value to a GraphQL type, or produces errors.
454458
coerceInputValue,
455459
// Coerces a GraphQL literal (AST) to a GraphQL type, or returns undefined.

src/type/__tests__/definition-test.ts

+20-10
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { identityFunc } from '../../jsutils/identityFunc.js';
55
import { inspect } from '../../jsutils/inspect.js';
66

77
import { Kind } from '../../language/kinds.js';
8-
import { parseValue } from '../../language/parser.js';
8+
import { parseConstValue } from '../../language/parser.js';
99

1010
import type { GraphQLNullableType, GraphQLType } from '../definition.js';
1111
import {
@@ -55,13 +55,13 @@ describe('Type System: Scalars', () => {
5555
).not.to.throw();
5656
});
5757

58-
it('accepts a Scalar type defining parseValue and parseLiteral', () => {
58+
it('accepts a Scalar type defining parseValue and parseConstLiteral', () => {
5959
expect(
6060
() =>
6161
new GraphQLScalarType({
6262
name: 'SomeScalar',
6363
parseValue: dummyFunc,
64-
parseLiteral: dummyFunc,
64+
parseConstLiteral: dummyFunc,
6565
}),
6666
).to.not.throw();
6767
});
@@ -72,25 +72,23 @@ describe('Type System: Scalars', () => {
7272
expect(scalar.serialize).to.equal(identityFunc);
7373
expect(scalar.parseValue).to.equal(identityFunc);
7474
expect(scalar.parseLiteral).to.be.a('function');
75+
expect(scalar.parseConstLiteral).to.be.a('function');
7576
});
7677

77-
it('use parseValue for parsing literals if parseLiteral omitted', () => {
78+
it('use parseValue for parsing literals if parseConstLiteral omitted', () => {
7879
const scalar = new GraphQLScalarType({
7980
name: 'Foo',
8081
parseValue(value) {
8182
return 'parseValue: ' + inspect(value);
8283
},
8384
});
8485

85-
expect(scalar.parseLiteral(parseValue('null'))).to.equal(
86+
expect(scalar.parseConstLiteral(parseConstValue('null'))).to.equal(
8687
'parseValue: null',
8788
);
88-
expect(scalar.parseLiteral(parseValue('{ foo: "bar" }'))).to.equal(
89-
'parseValue: { foo: "bar" }',
90-
);
9189
expect(
92-
scalar.parseLiteral(parseValue('{ foo: { bar: $var } }'), { var: 'baz' }),
93-
).to.equal('parseValue: { foo: { bar: "baz" } }');
90+
scalar.parseConstLiteral(parseConstValue('{ foo: "bar" }')),
91+
).to.equal('parseValue: { foo: "bar" }');
9492
});
9593

9694
it('rejects a Scalar type defining parseLiteral but not parseValue', () => {
@@ -104,6 +102,18 @@ describe('Type System: Scalars', () => {
104102
'SomeScalar must provide both "parseValue" and "parseLiteral" functions.',
105103
);
106104
});
105+
106+
it('rejects a Scalar type defining parseConstLiteral but not parseValue', () => {
107+
expect(
108+
() =>
109+
new GraphQLScalarType({
110+
name: 'SomeScalar',
111+
parseConstLiteral: dummyFunc,
112+
}),
113+
).to.throw(
114+
'SomeScalar must provide both "parseValue" and "parseConstLiteral" functions.',
115+
);
116+
});
107117
});
108118

109119
describe('Type System: Objects', () => {

0 commit comments

Comments
 (0)