Skip to content

Commit

Permalink
bring back valueFromAST but deprecate it
Browse files Browse the repository at this point in the history
  • Loading branch information
yaacovCR committed Sep 16, 2024
1 parent ec0ca36 commit b80ff6d
Show file tree
Hide file tree
Showing 4 changed files with 473 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -439,6 +439,9 @@ export {
printIntrospectionSchema,
// Create a GraphQLType from a GraphQL language AST.
typeFromAST,
// Create a JavaScript value from a GraphQL language AST with a Type.
/** @deprecated use `coerceInputLiteral()` instead - will be removed in v18 */
valueFromAST,
// Create a JavaScript value from a GraphQL language AST without a Type.
valueFromASTUntyped,
// Create a GraphQL language AST from a JavaScript value.
Expand Down
289 changes: 289 additions & 0 deletions src/utilities/__tests__/valueFromAST-test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,289 @@
import { assert, expect } from 'chai';
import { describe, it } from 'mocha';

import { identityFunc } from '../../jsutils/identityFunc.js';
import type { ObjMap } from '../../jsutils/ObjMap.js';

import { parseValue } from '../../language/parser.js';

import type { GraphQLInputType } from '../../type/definition.js';
import {
GraphQLEnumType,
GraphQLInputObjectType,
GraphQLList,
GraphQLNonNull,
GraphQLScalarType,
} from '../../type/definition.js';
import {
GraphQLBoolean,
GraphQLFloat,
GraphQLID,
GraphQLInt,
GraphQLString,
} from '../../type/scalars.js';

import { valueFromAST } from '../valueFromAST.js';

/** @deprecated use `coerceInputLiteral()` instead - will be removed in v18 */
describe('valueFromAST', () => {
function expectValueFrom(
valueText: string,
type: GraphQLInputType,
variables?: ObjMap<unknown>,
) {
const ast = parseValue(valueText);
const value = valueFromAST(ast, type, variables);
return expect(value);
}

it('rejects empty input', () => {
expect(valueFromAST(null, GraphQLBoolean)).to.deep.equal(undefined);
});

it('converts according to input coercion rules', () => {
expectValueFrom('true', GraphQLBoolean).to.equal(true);
expectValueFrom('false', GraphQLBoolean).to.equal(false);
expectValueFrom('123', GraphQLInt).to.equal(123);
expectValueFrom('123', GraphQLFloat).to.equal(123);
expectValueFrom('123.456', GraphQLFloat).to.equal(123.456);
expectValueFrom('"abc123"', GraphQLString).to.equal('abc123');
expectValueFrom('123456', GraphQLID).to.equal('123456');
expectValueFrom('"123456"', GraphQLID).to.equal('123456');
});

it('does not convert when input coercion rules reject a value', () => {
expectValueFrom('123', GraphQLBoolean).to.equal(undefined);
expectValueFrom('123.456', GraphQLInt).to.equal(undefined);
expectValueFrom('true', GraphQLInt).to.equal(undefined);
expectValueFrom('"123"', GraphQLInt).to.equal(undefined);
expectValueFrom('"123"', GraphQLFloat).to.equal(undefined);
expectValueFrom('123', GraphQLString).to.equal(undefined);
expectValueFrom('true', GraphQLString).to.equal(undefined);
expectValueFrom('123.456', GraphQLString).to.equal(undefined);
});

it('convert using parseLiteral from a custom scalar type', () => {
const passthroughScalar = new GraphQLScalarType({
name: 'PassthroughScalar',
parseLiteral(node) {
assert(node.kind === 'StringValue');
return node.value;
},
parseValue: identityFunc,
});

expectValueFrom('"value"', passthroughScalar).to.equal('value');

const throwScalar = new GraphQLScalarType({
name: 'ThrowScalar',
parseLiteral() {
throw new Error('Test');
},
parseValue: identityFunc,
});

expectValueFrom('value', throwScalar).to.equal(undefined);

const returnUndefinedScalar = new GraphQLScalarType({
name: 'ReturnUndefinedScalar',
parseLiteral() {
return undefined;
},
parseValue: identityFunc,
});

expectValueFrom('value', returnUndefinedScalar).to.equal(undefined);
});

it('converts enum values according to input coercion rules', () => {
const testEnum = new GraphQLEnumType({
name: 'TestColor',
values: {
RED: { value: 1 },
GREEN: { value: 2 },
BLUE: { value: 3 },
NULL: { value: null },
NAN: { value: NaN },
NO_CUSTOM_VALUE: { value: undefined },
},
});

expectValueFrom('RED', testEnum).to.equal(1);
expectValueFrom('BLUE', testEnum).to.equal(3);
expectValueFrom('3', testEnum).to.equal(undefined);
expectValueFrom('"BLUE"', testEnum).to.equal(undefined);
expectValueFrom('null', testEnum).to.equal(null);
expectValueFrom('NULL', testEnum).to.equal(null);
expectValueFrom('NULL', new GraphQLNonNull(testEnum)).to.equal(null);
expectValueFrom('NAN', testEnum).to.deep.equal(NaN);
expectValueFrom('NO_CUSTOM_VALUE', testEnum).to.equal('NO_CUSTOM_VALUE');
});

// Boolean!
const nonNullBool = new GraphQLNonNull(GraphQLBoolean);
// [Boolean]
const listOfBool = new GraphQLList(GraphQLBoolean);
// [Boolean!]
const listOfNonNullBool = new GraphQLList(nonNullBool);
// [Boolean]!
const nonNullListOfBool = new GraphQLNonNull(listOfBool);
// [Boolean!]!
const nonNullListOfNonNullBool = new GraphQLNonNull(listOfNonNullBool);

it('coerces to null unless non-null', () => {
expectValueFrom('null', GraphQLBoolean).to.equal(null);
expectValueFrom('null', nonNullBool).to.equal(undefined);
});

it('coerces lists of values', () => {
expectValueFrom('true', listOfBool).to.deep.equal([true]);
expectValueFrom('123', listOfBool).to.equal(undefined);
expectValueFrom('null', listOfBool).to.equal(null);
expectValueFrom('[true, false]', listOfBool).to.deep.equal([true, false]);
expectValueFrom('[true, 123]', listOfBool).to.equal(undefined);
expectValueFrom('[true, null]', listOfBool).to.deep.equal([true, null]);
expectValueFrom('{ true: true }', listOfBool).to.equal(undefined);
});

it('coerces non-null lists of values', () => {
expectValueFrom('true', nonNullListOfBool).to.deep.equal([true]);
expectValueFrom('123', nonNullListOfBool).to.equal(undefined);
expectValueFrom('null', nonNullListOfBool).to.equal(undefined);
expectValueFrom('[true, false]', nonNullListOfBool).to.deep.equal([
true,
false,
]);
expectValueFrom('[true, 123]', nonNullListOfBool).to.equal(undefined);
expectValueFrom('[true, null]', nonNullListOfBool).to.deep.equal([
true,
null,
]);
});

it('coerces lists of non-null values', () => {
expectValueFrom('true', listOfNonNullBool).to.deep.equal([true]);
expectValueFrom('123', listOfNonNullBool).to.equal(undefined);
expectValueFrom('null', listOfNonNullBool).to.equal(null);
expectValueFrom('[true, false]', listOfNonNullBool).to.deep.equal([
true,
false,
]);
expectValueFrom('[true, 123]', listOfNonNullBool).to.equal(undefined);
expectValueFrom('[true, null]', listOfNonNullBool).to.equal(undefined);
});

it('coerces non-null lists of non-null values', () => {
expectValueFrom('true', nonNullListOfNonNullBool).to.deep.equal([true]);
expectValueFrom('123', nonNullListOfNonNullBool).to.equal(undefined);
expectValueFrom('null', nonNullListOfNonNullBool).to.equal(undefined);
expectValueFrom('[true, false]', nonNullListOfNonNullBool).to.deep.equal([
true,
false,
]);
expectValueFrom('[true, 123]', nonNullListOfNonNullBool).to.equal(
undefined,
);
expectValueFrom('[true, null]', nonNullListOfNonNullBool).to.equal(
undefined,
);
});

const testInputObj = new GraphQLInputObjectType({
name: 'TestInput',
fields: {
int: { type: GraphQLInt, defaultValue: 42 },
bool: { type: GraphQLBoolean },
requiredBool: { type: nonNullBool },
},
});
const testOneOfInputObj = new GraphQLInputObjectType({
name: 'TestOneOfInput',
fields: {
a: { type: GraphQLString },
b: { type: GraphQLString },
},
isOneOf: true,
});

it('coerces input objects according to input coercion rules', () => {
expectValueFrom('null', testInputObj).to.equal(null);
expectValueFrom('123', testInputObj).to.equal(undefined);
expectValueFrom('[]', testInputObj).to.equal(undefined);
expectValueFrom(
'{ int: 123, requiredBool: false }',
testInputObj,
).to.deep.equal({
int: 123,
requiredBool: false,
});
expectValueFrom(
'{ bool: true, requiredBool: false }',
testInputObj,
).to.deep.equal({
int: 42,
bool: true,
requiredBool: false,
});
expectValueFrom('{ int: true, requiredBool: true }', testInputObj).to.equal(
undefined,
);
expectValueFrom('{ requiredBool: null }', testInputObj).to.equal(undefined);
expectValueFrom('{ bool: true }', testInputObj).to.equal(undefined);
expectValueFrom('{ a: "abc" }', testOneOfInputObj).to.deep.equal({
a: 'abc',
});
expectValueFrom('{ b: "def" }', testOneOfInputObj).to.deep.equal({
b: 'def',
});
expectValueFrom('{ a: "abc", b: null }', testOneOfInputObj).to.deep.equal(
undefined,
);
expectValueFrom('{ a: null }', testOneOfInputObj).to.equal(undefined);
expectValueFrom('{ a: 1 }', testOneOfInputObj).to.equal(undefined);
expectValueFrom('{ a: "abc", b: "def" }', testOneOfInputObj).to.equal(
undefined,
);
expectValueFrom('{}', testOneOfInputObj).to.equal(undefined);
expectValueFrom('{ c: "abc" }', testOneOfInputObj).to.equal(undefined);
});

it('accepts variable values assuming already coerced', () => {
expectValueFrom('$var', GraphQLBoolean, {}).to.equal(undefined);
expectValueFrom('$var', GraphQLBoolean, { var: true }).to.equal(true);
expectValueFrom('$var', GraphQLBoolean, { var: null }).to.equal(null);
expectValueFrom('$var', nonNullBool, { var: null }).to.equal(undefined);
});

it('asserts variables are provided as items in lists', () => {
expectValueFrom('[ $foo ]', listOfBool, {}).to.deep.equal([null]);
expectValueFrom('[ $foo ]', listOfNonNullBool, {}).to.equal(undefined);
expectValueFrom('[ $foo ]', listOfNonNullBool, {
foo: true,
}).to.deep.equal([true]);
// Note: variables are expected to have already been coerced, so we
// do not expect the singleton wrapping behavior for variables.
expectValueFrom('$foo', listOfNonNullBool, { foo: true }).to.equal(true);
expectValueFrom('$foo', listOfNonNullBool, { foo: [true] }).to.deep.equal([
true,
]);
});

it('omits input object fields for unprovided variables', () => {
expectValueFrom(
'{ int: $foo, bool: $foo, requiredBool: true }',
testInputObj,
{},
).to.deep.equal({ int: 42, requiredBool: true });

expectValueFrom('{ requiredBool: $foo }', testInputObj, {}).to.equal(
undefined,
);

expectValueFrom('{ requiredBool: $foo }', testInputObj, {
foo: true,
}).to.deep.equal({
int: 42,
requiredBool: true,
});
});
});
6 changes: 6 additions & 0 deletions src/utilities/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,12 @@ export {
// Create a GraphQLType from a GraphQL language AST.
export { typeFromAST } from './typeFromAST.js';

// Create a JavaScript value from a GraphQL language AST with a type.
export {
/** @deprecated use `coerceInputLiteral()` instead - will be removed in v18 */
valueFromAST,
} from './valueFromAST.js';

// Create a JavaScript value from a GraphQL language AST without a type.
export { valueFromASTUntyped } from './valueFromASTUntyped.js';

Expand Down
Loading

0 comments on commit b80ff6d

Please sign in to comment.