Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
leebyron committed May 6, 2021
1 parent f381c40 commit 55e3e32
Show file tree
Hide file tree
Showing 22 changed files with 316 additions and 51 deletions.
3 changes: 3 additions & 0 deletions src/index.d.ts
Expand Up @@ -407,6 +407,7 @@ export {
// DEPRECATED: use coerceInputLiteral
valueFromAST,
// Create a JavaScript value from a GraphQL language AST without a Type.
// DEPRECATED: use literalToValue
valueFromASTUntyped,
// Create a GraphQL language AST from a JavaScript value.
// DEPRECATED: use valueToLiteral
Expand All @@ -417,6 +418,8 @@ export {
visitWithTypeInfo,
// Create a GraphQL Literal AST from a JavaScript input value.
valueToLiteral,
// Create a JavaScript input value from a GraphQL Literal AST.
literalToValue,
// Coerces a GraphQL Literal with a GraphQL type.
coerceInputLiteral,
// Coerces a JavaScript value with a GraphQL type, or produces errors.
Expand Down
3 changes: 3 additions & 0 deletions src/index.js
Expand Up @@ -396,6 +396,7 @@ export {
// DEPRECATED: use coerceInputLiteral
valueFromAST,
// Create a JavaScript value from a GraphQL language AST without a Type.
// DEPRECATED: use literalToValue
valueFromASTUntyped,
// Create a GraphQL language AST from a JavaScript value.
// DEPRECATED: use valueToLiteral
Expand All @@ -406,6 +407,8 @@ export {
visitWithTypeInfo,
// Create a GraphQL Literal AST from a JavaScript input value.
valueToLiteral,
// Create a JavaScript input value from a GraphQL Literal AST.
literalToValue,
// Coerces a GraphQL Literal with a GraphQL type.
coerceInputLiteral,
// Coerces a JavaScript value with a GraphQL type, or produces errors.
Expand Down
12 changes: 7 additions & 5 deletions src/type/__tests__/definition-test.js
Expand Up @@ -17,6 +17,7 @@ import {
GraphQLEnumType,
GraphQLInputObjectType,
} from '../definition';
import { coerceInputLiteral } from '../../utilities/coerceInputLiteral';

const ScalarType = new GraphQLScalarType({ name: 'Scalar' });
const ObjectType = new GraphQLObjectType({ name: 'Object', fields: {} });
Expand Down Expand Up @@ -72,7 +73,6 @@ describe('Type System: Scalars', () => {

expect(scalar.serialize).to.equal(identityFunc);
expect(scalar.parseValue).to.equal(identityFunc);
expect(scalar.parseLiteral).to.be.a('function');
});

it('use parseValue for parsing literals if parseLiteral omitted', () => {
Expand All @@ -83,14 +83,16 @@ describe('Type System: Scalars', () => {
},
});

expect(scalar.parseLiteral(parseValue('null'))).to.equal(
'parseValue: null',
expect(coerceInputLiteral(parseValue('null'), scalar)).to.equal(
null,
);
expect(scalar.parseLiteral(parseValue('{ foo: "bar" }'))).to.equal(
expect(coerceInputLiteral(parseValue('{ foo: "bar" }'), scalar)).to.equal(
'parseValue: { foo: "bar" }',
);
expect(
scalar.parseLiteral(parseValue('{ foo: { bar: $var } }'), { var: 'baz' }),
coerceInputLiteral(parseValue('{ foo: { bar: $var } }'), scalar, {
var: 'baz',
}),
).to.equal('parseValue: { foo: { bar: "baz" } }');
});

Expand Down
12 changes: 6 additions & 6 deletions src/type/__tests__/scalars-test.js
@@ -1,7 +1,7 @@
import { expect } from 'chai';
import { describe, it } from 'mocha';

import { parseValue as parseValueToAST } from '../../language/parser';
import { parseConstValue } from '../../language/parser';

import {
GraphQLID,
Expand Down Expand Up @@ -66,7 +66,7 @@ describe('Type System: Specified scalar types', () => {

it('parseLiteral', () => {
function parseLiteral(str: string) {
return GraphQLInt.parseLiteral(parseValueToAST(str), undefined);
return GraphQLInt.parseLiteral?.(parseConstValue(str));
}

expect(parseLiteral('1')).to.equal(1);
Expand Down Expand Up @@ -231,7 +231,7 @@ describe('Type System: Specified scalar types', () => {

it('parseLiteral', () => {
function parseLiteral(str: string) {
return GraphQLFloat.parseLiteral(parseValueToAST(str), undefined);
return GraphQLFloat.parseLiteral?.(parseConstValue(str));
}

expect(parseLiteral('1')).to.equal(1);
Expand Down Expand Up @@ -344,7 +344,7 @@ describe('Type System: Specified scalar types', () => {

it('parseLiteral', () => {
function parseLiteral(str: string) {
return GraphQLString.parseLiteral(parseValueToAST(str), undefined);
return GraphQLString.parseLiteral?.(parseConstValue(str));
}

expect(parseLiteral('"foo"')).to.equal('foo');
Expand Down Expand Up @@ -456,7 +456,7 @@ describe('Type System: Specified scalar types', () => {

it('parseLiteral', () => {
function parseLiteral(str: string) {
return GraphQLBoolean.parseLiteral(parseValueToAST(str), undefined);
return GraphQLBoolean.parseLiteral?.(parseConstValue(str));
}

expect(parseLiteral('true')).to.equal(true);
Expand Down Expand Up @@ -571,7 +571,7 @@ describe('Type System: Specified scalar types', () => {

it('parseLiteral', () => {
function parseLiteral(str: string) {
return GraphQLID.parseLiteral(parseValueToAST(str), undefined);
return GraphQLID.parseLiteral?.(parseConstValue(str));
}

expect(parseLiteral('""')).to.equal('');
Expand Down
24 changes: 13 additions & 11 deletions src/type/definition.d.ts
Expand Up @@ -22,7 +22,6 @@ import {
OperationDefinitionNode,
FieldNode,
FragmentDefinitionNode,
ValueNode,
ConstValueNode,
ScalarTypeExtensionNode,
UnionTypeExtensionNode,
Expand Down Expand Up @@ -316,8 +315,9 @@ export class GraphQLScalarType {
specifiedByURL: Maybe<string>;
serialize: GraphQLScalarSerializer<unknown>;
parseValue: GraphQLScalarValueParser<unknown>;
parseLiteral: GraphQLScalarLiteralParser<unknown>;
parseLiteral: Maybe<GraphQLScalarLiteralParser<unknown>>;
valueToLiteral: Maybe<GraphQLScalarValueToLiteral>;
literalToValue: Maybe<GraphQLScalarLiteralToValue>;
extensions: Maybe<Readonly<GraphQLScalarTypeExtensions>>;
astNode: Maybe<ScalarTypeDefinitionNode>;
extensionASTNodes: ReadonlyArray<ScalarTypeExtensionNode>;
Expand All @@ -328,8 +328,9 @@ export class GraphQLScalarType {
specifiedByURL: Maybe<string>;
serialize: GraphQLScalarSerializer<unknown>;
parseValue: GraphQLScalarValueParser<unknown>;
parseLiteral: GraphQLScalarLiteralParser<unknown>;
parseLiteral: Maybe<GraphQLScalarLiteralParser<unknown>>;
valueToLiteral: Maybe<GraphQLScalarValueToLiteral>;
literalToValue: Maybe<GraphQLScalarLiteralToValue>;
extensions: Maybe<Readonly<GraphQLScalarTypeExtensions>>;
extensionASTNodes: ReadonlyArray<ScalarTypeExtensionNode>;
};
Expand All @@ -346,13 +347,14 @@ export type GraphQLScalarValueParser<TInternal> = (
value: unknown,
) => Maybe<TInternal>;
export type GraphQLScalarLiteralParser<TInternal> = (
valueNode: ValueNode,
variables: Maybe<ObjMap<unknown>>,
valueNode: ConstValueNode,
) => Maybe<TInternal>;

export type GraphQLScalarValueToLiteral = (
inputValue: unknown,
) => Maybe<ConstValueNode>;
export type GraphQLScalarLiteralToValue = (
valueNode: ConstValueNode,
) => unknown;

export interface GraphQLScalarTypeConfig<TInternal, TExternal> {
name: string;
Expand All @@ -364,8 +366,10 @@ export interface GraphQLScalarTypeConfig<TInternal, TExternal> {
parseValue?: GraphQLScalarValueParser<TInternal>;
// Parses an externally provided literal value to use as an input.
parseLiteral?: GraphQLScalarLiteralParser<TInternal>;
// Translates an external input value to an external literal (AST).
// Translates an external input value to a literal (AST).
valueToLiteral?: Maybe<GraphQLScalarValueToLiteral>;
// Translates a literal (AST) to external input value.
literalToValue?: Maybe<GraphQLScalarLiteralToValue>;
extensions?: Maybe<Readonly<GraphQLScalarTypeExtensions>>;
astNode?: Maybe<ScalarTypeDefinitionNode>;
extensionASTNodes?: Maybe<ReadonlyArray<ScalarTypeExtensionNode>>;
Expand Down Expand Up @@ -791,11 +795,9 @@ export class GraphQLEnumType {
getValue(name: string): Maybe<GraphQLEnumValue>;
serialize(value: unknown): Maybe<string>;
parseValue(value: unknown): Maybe<any>;
parseLiteral(
valueNode: ValueNode,
_variables: Maybe<ObjMap<unknown>>,
): Maybe<any>;
parseLiteral(valueNode: ConstValueNode): Maybe<any>;
valueToLiteral(value: unknown): Maybe<ConstValueNode>;
literalToValue(valueNode: ConstValueNode): unknown;

toConfig(): GraphQLEnumTypeConfig & {
extensions: Maybe<Readonly<GraphQLEnumTypeExtensions>>;
Expand Down
31 changes: 19 additions & 12 deletions src/type/definition.js
Expand Up @@ -45,8 +45,6 @@ import type {
ConstValueNode,
} from '../language/ast';

import { valueFromASTUntyped } from '../utilities/valueFromASTUntyped';

import type { GraphQLSchema } from './schema';

// Predicates & Assertions
Expand Down Expand Up @@ -562,8 +560,9 @@ export class GraphQLScalarType {
specifiedByURL: ?string;
serialize: GraphQLScalarSerializer<mixed>;
parseValue: GraphQLScalarValueParser<mixed>;
parseLiteral: GraphQLScalarLiteralParser<mixed>;
parseLiteral: ?GraphQLScalarLiteralParser<mixed>;
valueToLiteral: ?GraphQLScalarValueToLiteral;
literalToValue: ?GraphQLScalarLiteralToValue;
extensions: ?ReadOnlyObjMap<mixed>;
astNode: ?ScalarTypeDefinitionNode;
extensionASTNodes: $ReadOnlyArray<ScalarTypeExtensionNode>;
Expand All @@ -575,10 +574,9 @@ export class GraphQLScalarType {
this.specifiedByURL = config.specifiedByURL;
this.serialize = config.serialize ?? identityFunc;
this.parseValue = parseValue;
this.parseLiteral =
config.parseLiteral ??
((node, variables) => parseValue(valueFromASTUntyped(node, variables)));
this.parseLiteral = config.parseLiteral;
this.valueToLiteral = config.valueToLiteral;
this.literalToValue = config.literalToValue;
this.extensions = config.extensions && toObjMap(config.extensions);
this.astNode = config.astNode;
this.extensionASTNodes = config.extensionASTNodes ?? [];
Expand Down Expand Up @@ -615,6 +613,7 @@ export class GraphQLScalarType {
parseValue: this.parseValue,
parseLiteral: this.parseLiteral,
valueToLiteral: this.valueToLiteral,
literalToValue: this.literalToValue,
extensions: this.extensions,
astNode: this.astNode,
extensionASTNodes: this.extensionASTNodes,
Expand Down Expand Up @@ -644,14 +643,15 @@ export type GraphQLScalarValueParser<TInternal> = (
) => ?TInternal;

export type GraphQLScalarLiteralParser<TInternal> = (
valueNode: ValueNode,
variables: ?ObjMap<mixed>,
valueNode: ConstValueNode,
) => ?TInternal;

export type GraphQLScalarValueToLiteral = (
inputValue: mixed,
) => ?ConstValueNode;

export type GraphQLScalarLiteralToValue = (valueNode: ConstValueNode) => mixed;

export type GraphQLScalarTypeConfig<TInternal, TExternal> = {|
name: string,
description?: ?string,
Expand All @@ -661,9 +661,11 @@ export type GraphQLScalarTypeConfig<TInternal, TExternal> = {|
// Parses an externally provided value to use as an input.
parseValue?: GraphQLScalarValueParser<TInternal>,
// Parses an externally provided literal value to use as an input.
parseLiteral?: GraphQLScalarLiteralParser<TInternal>,
// Translates an external input value to an external literal (AST).
parseLiteral?: ?GraphQLScalarLiteralParser<TInternal>,
// Translates an external input value to a literal (AST).
valueToLiteral?: ?GraphQLScalarValueToLiteral,
// Translates a literal (AST) to external input value.
literalToValue?: ?GraphQLScalarLiteralToValue,
extensions?: ?ReadOnlyObjMapLike<mixed>,
astNode?: ?ScalarTypeDefinitionNode,
extensionASTNodes?: ?$ReadOnlyArray<ScalarTypeExtensionNode>,
Expand All @@ -673,7 +675,6 @@ type GraphQLScalarTypeNormalizedConfig = {|
...GraphQLScalarTypeConfig<mixed, mixed>,
serialize: GraphQLScalarSerializer<mixed>,
parseValue: GraphQLScalarValueParser<mixed>,
parseLiteral: GraphQLScalarLiteralParser<mixed>,
extensions: ?ReadOnlyObjMap<mixed>,
extensionASTNodes: $ReadOnlyArray<ScalarTypeExtensionNode>,
|};
Expand Down Expand Up @@ -1331,7 +1332,7 @@ export class GraphQLEnumType /* <T> */ {
return enumValue.value;
}

parseLiteral(valueNode: ValueNode, _variables: ?ObjMap<mixed>): ?any /* T */ {
parseLiteral(valueNode: ValueNode): ?any /* T */ {
// Note: variables will be resolved to a value before calling this function.
if (valueNode.kind !== Kind.ENUM) {
const valueStr = print(valueNode);
Expand Down Expand Up @@ -1364,6 +1365,12 @@ export class GraphQLEnumType /* <T> */ {
}
}

literalToValue(valueNode: ConstValueNode): mixed {
if (valueNode.kind === Kind.ENUM) {
return valueNode.value;
}
}

toConfig(): GraphQLEnumTypeNormalizedConfig {
const values = keyValMap(
this.getValues(),
Expand Down
6 changes: 6 additions & 0 deletions src/type/scalars.js
Expand Up @@ -278,6 +278,12 @@ export const GraphQLID: GraphQLScalarType = new GraphQLScalarType({
return { kind: Kind.STRING, value };
}
},
literalToValue(valueNode: ConstValueNode): mixed {
// ID Int literals are represented as string values.
if (valueNode.kind === Kind.INT || valueNode.kind === Kind.STRING) {
return valueNode.value;
}
},
});

export const specifiedScalarTypes: $ReadOnlyArray<GraphQLScalarType> = Object.freeze(
Expand Down
26 changes: 26 additions & 0 deletions src/utilities/__tests__/literalToValue-test.js
@@ -0,0 +1,26 @@
import { expect } from 'chai';
import { describe, it } from 'mocha';

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

import { parseConstValue } from '../../language/parser';
import { literalToValue } from '../literalToValue';

describe('literalToValue', () => {
it('converts null', () => {
expect(literalToValue(parseConstValue('null'))).to.equal(null);
});
});
42 changes: 42 additions & 0 deletions src/utilities/__tests__/replaceASTVariables-test.js
@@ -0,0 +1,42 @@
import { expect } from 'chai';
import { describe, it } from 'mocha';

import type { ValueNode } from '../../language/ast';
import { parseValue as _parseValue } from '../../language/parser';
import { replaceASTVariables } from '../replaceASTVariables';

function parseValue(ast: string): ValueNode {
return _parseValue(ast, { noLocation: true });
}

describe('replaceASTVariables', () => {
it('does not change simple AST', () => {
const ast = parseValue('null')
expect(replaceASTVariables(ast, undefined)).to.equal(ast);
});

it('replaces simple Variables', () => {
const ast = parseValue('$var')
expect(replaceASTVariables(ast, {var:123})).to.deep.equal(parseValue('123'));
});

it('replaces nested Variables', () => {
const ast = parseValue('{ foo: [ $var ], bar: $var }')
expect(replaceASTVariables(ast, {var:123})).to.deep.equal(parseValue('{ foo: [ 123 ], bar: 123 }'));
});

it('replaces missing Variables with null', () => {
const ast = parseValue('$var')
expect(replaceASTVariables(ast, undefined)).to.deep.equal(parseValue('null'));
});

it('replaces missing Variables in lists with null', () => {
const ast = parseValue('[1, $var]')
expect(replaceASTVariables(ast, undefined)).to.deep.equal(parseValue('[1, null]'));
});

it('omits missing Variables from objects', () => {
const ast = parseValue('{ foo: 1, bar: $var }')
expect(replaceASTVariables(ast, undefined)).to.deep.equal(parseValue('{ foo: 1 }'));
});
});

0 comments on commit 55e3e32

Please sign in to comment.