From 222caafc344ecd70d155317c2e6da9ba5f9a65fa Mon Sep 17 00:00:00 2001 From: Lee Byron Date: Mon, 10 May 2021 11:44:12 -0700 Subject: [PATCH] Preserve defaultValue literals Fixes #3051 --- src/execution/values.js | 15 ++++++--- src/index.d.ts | 4 +++ src/index.js | 4 +++ src/type/__tests__/predicate-test.js | 31 +++++++------------ src/type/defaultValues.d.ts | 13 ++++++++ src/type/defaultValues.js | 28 +++++++++++++++++ src/type/definition.d.ts | 8 ++++- src/type/definition.js | 21 +++++++++++-- src/type/index.d.ts | 6 ++++ src/type/index.js | 6 ++++ src/type/introspection.js | 16 ++++++---- src/utilities/TypeInfo.js | 6 ++-- src/utilities/__tests__/astFromValue-test.js | 2 ++ .../__tests__/buildClientSchema-test.js | 22 +++++++++++++ src/utilities/__tests__/valueFromAST-test.js | 15 +++++++++ src/utilities/astFromValue.js | 12 ++++--- src/utilities/buildClientSchema.js | 12 +++---- src/utilities/coerceInputValue.js | 8 +++-- src/utilities/extendSchema.js | 4 +-- src/utilities/findBreakingChanges.js | 23 +++++++------- src/utilities/printSchema.js | 10 +++--- src/utilities/valueFromAST.js | 9 ++++-- 22 files changed, 202 insertions(+), 73 deletions(-) create mode 100644 src/type/defaultValues.d.ts create mode 100644 src/type/defaultValues.js diff --git a/src/execution/values.js b/src/execution/values.js index 3b6728254ba..173f04c65ae 100644 --- a/src/execution/values.js +++ b/src/execution/values.js @@ -17,6 +17,7 @@ import type { GraphQLSchema } from '../type/schema'; import type { GraphQLField } from '../type/definition'; import type { GraphQLDirective } from '../type/directives'; import { isInputType, isNonNullType } from '../type/definition'; +import { getCoercedDefaultValue } from '../type/defaultValues'; import { typeFromAST } from '../utilities/typeFromAST'; import { valueFromAST } from '../utilities/valueFromAST'; @@ -173,8 +174,11 @@ export function getArgumentValues( const argumentNode = argNodeMap[name]; if (!argumentNode) { - if (argDef.defaultValue !== undefined) { - coercedValues[name] = argDef.defaultValue; + if (argDef.defaultValue) { + coercedValues[name] = getCoercedDefaultValue( + argDef.defaultValue, + argDef.type, + ); } else if (isNonNullType(argType)) { throw new GraphQLError( `Argument "${name}" of required type "${inspect(argType)}" ` + @@ -194,8 +198,11 @@ export function getArgumentValues( variableValues == null || !hasOwnProperty(variableValues, variableName) ) { - if (argDef.defaultValue !== undefined) { - coercedValues[name] = argDef.defaultValue; + if (argDef.defaultValue) { + coercedValues[name] = getCoercedDefaultValue( + argDef.defaultValue, + argDef.type, + ); } else if (isNonNullType(argType)) { throw new GraphQLError( `Argument "${name}" of required type "${inspect(argType)}" ` + diff --git a/src/index.d.ts b/src/index.d.ts index 317602293e5..db8241b04e2 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -126,6 +126,9 @@ export { // Validate GraphQL schema. validateSchema, assertValidSchema, + // Operate on default values. + getCoercedDefaultValue, + getLiteralDefaultValue, } from './type/index'; export { @@ -150,6 +153,7 @@ export { GraphQLArgumentConfig, GraphQLArgumentExtensions, GraphQLInputValue, + GraphQLDefaultValueUsage, GraphQLInputValueConfig, GraphQLEnumTypeConfig, GraphQLEnumTypeExtensions, diff --git a/src/index.js b/src/index.js index b46f10cd3a0..71dbddf0fa3 100644 --- a/src/index.js +++ b/src/index.js @@ -125,6 +125,9 @@ export { // Validate GraphQL schema. validateSchema, assertValidSchema, + // Operate on default values. + getCoercedDefaultValue, + getLiteralDefaultValue, } from './type/index'; export type { @@ -146,6 +149,7 @@ export type { GraphQLArgument, GraphQLArgumentConfig, GraphQLInputValue, + GraphQLDefaultValueUsage, GraphQLInputValueConfig, GraphQLEnumTypeConfig, GraphQLEnumValue, diff --git a/src/type/__tests__/predicate-test.js b/src/type/__tests__/predicate-test.js index 899752864cd..2ac688b4c8b 100644 --- a/src/type/__tests__/predicate-test.js +++ b/src/type/__tests__/predicate-test.js @@ -69,6 +69,7 @@ import { assertNamedType, getNullableType, getNamedType, + defineInputValue, } from '../definition'; const ObjectType = new GraphQLObjectType({ name: 'Object', fields: {} }); @@ -562,19 +563,14 @@ describe('Type predicates', () => { }); describe('isRequiredInput', () => { - function buildArg(config: {| + function buildArg({ + type, + defaultValue, + }: {| type: GraphQLInputType, defaultValue?: mixed, |}): GraphQLArgument { - return { - name: 'someArg', - type: config.type, - description: undefined, - defaultValue: config.defaultValue, - deprecationReason: null, - extensions: undefined, - astNode: undefined, - }; + return defineInputValue({ type, defaultValue }, 'someArg'); } it('returns true for required arguments', () => { @@ -608,19 +604,14 @@ describe('Type predicates', () => { expect(isRequiredInput(optArg4)).to.equal(false); }); - function buildInputField(config: {| + function buildInputField({ + type, + defaultValue, + }: {| type: GraphQLInputType, defaultValue?: mixed, |}): GraphQLInputField { - return { - name: 'someInputField', - type: config.type, - description: undefined, - defaultValue: config.defaultValue, - deprecationReason: null, - extensions: undefined, - astNode: undefined, - }; + return defineInputValue({ type, defaultValue }, 'someInputField'); } it('returns true for required input field', () => { diff --git a/src/type/defaultValues.d.ts b/src/type/defaultValues.d.ts new file mode 100644 index 00000000000..363e1534862 --- /dev/null +++ b/src/type/defaultValues.d.ts @@ -0,0 +1,13 @@ +import { ConstValueNode } from '../language/ast'; + +import { GraphQLInputType, GraphQLDefaultValueUsage } from './definition'; + +export function getLiteralDefaultValue( + usage: GraphQLDefaultValueUsage, + type: GraphQLInputType, +): ConstValueNode; + +export function getCoercedDefaultValue( + usage: GraphQLDefaultValueUsage, + type: GraphQLInputType, +): unknown; diff --git a/src/type/defaultValues.js b/src/type/defaultValues.js new file mode 100644 index 00000000000..70769898c28 --- /dev/null +++ b/src/type/defaultValues.js @@ -0,0 +1,28 @@ +import { invariant } from '../jsutils/invariant'; +import type { ConstValueNode } from '../language/ast'; + +import { astFromValue } from '../utilities/astFromValue'; +import { valueFromAST } from '../utilities/valueFromAST'; + +import type { GraphQLInputType, GraphQLDefaultValueUsage } from './definition'; + +export function getLiteralDefaultValue( + usage: GraphQLDefaultValueUsage, + type: GraphQLInputType, +): ConstValueNode { + if (usage.literal) { + return usage.literal; + } + const literal = astFromValue(usage.value, type); + invariant(literal, 'Value cannot be converted to literal for this type'); + return literal; +} + +export function getCoercedDefaultValue( + usage: GraphQLDefaultValueUsage, + type: GraphQLInputType, +): mixed { + return usage.value !== undefined + ? usage.value + : valueFromAST(usage.literal, type); +} diff --git a/src/type/definition.d.ts b/src/type/definition.d.ts index f07202d91af..0acd9b3d4e7 100644 --- a/src/type/definition.d.ts +++ b/src/type/definition.d.ts @@ -23,6 +23,7 @@ import { FieldNode, FragmentDefinitionNode, ValueNode, + ConstValueNode, ScalarTypeExtensionNode, UnionTypeExtensionNode, EnumTypeExtensionNode, @@ -575,12 +576,17 @@ export interface GraphQLInputValue { name: string; description: Maybe; type: GraphQLInputType; - defaultValue: unknown; + defaultValue: Maybe; deprecationReason: Maybe; extensions: Maybe>; astNode: Maybe; } +export interface GraphQLDefaultValueUsage { + value: unknown; + literal: Maybe; +} + export interface GraphQLInputValueConfig { description?: Maybe; type: GraphQLInputType; diff --git a/src/type/definition.js b/src/type/definition.js index c16433d5b31..1242c3d183c 100644 --- a/src/type/definition.js +++ b/src/type/definition.js @@ -41,6 +41,7 @@ import type { FieldNode, FragmentDefinitionNode, ValueNode, + ConstValueNode, } from '../language/ast'; import { valueFromASTUntyped } from '../utilities/valueFromASTUntyped'; @@ -971,11 +972,18 @@ export function defineInputValue( !('resolve' in config), `${name} has a resolve property, but inputs cannot define resolvers.`, ); + let defaultValue; + if (config.defaultValue !== undefined || config.defaultValueLiteral) { + defaultValue = { + value: config.defaultValue, + literal: config.defaultValueLiteral, + }; + } return { name, description: config.description, type: config.type, - defaultValue: config.defaultValue, + defaultValue, deprecationReason: config.deprecationReason, extensions: config.extensions && toObjMap(config.extensions), astNode: config.astNode, @@ -991,7 +999,8 @@ export function inputValueToConfig( return { description: inputValue.description, type: inputValue.type, - defaultValue: inputValue.defaultValue, + defaultValue: inputValue.defaultValue?.value, + defaultValueLiteral: inputValue.defaultValue?.literal, deprecationReason: inputValue.deprecationReason, extensions: inputValue.extensions, astNode: inputValue.astNode, @@ -1002,16 +1011,22 @@ export type GraphQLInputValue = {| name: string, description: ?string, type: GraphQLInputType, - defaultValue: mixed, + defaultValue: ?GraphQLDefaultValueUsage, deprecationReason: ?string, extensions: ?ReadOnlyObjMap, astNode: ?InputValueDefinitionNode, |}; +export type GraphQLDefaultValueUsage = {| + value: mixed, + literal: ?ConstValueNode, +|}; + export type GraphQLInputValueConfig = {| description?: ?string, type: GraphQLInputType, defaultValue?: mixed, + defaultValueLiteral?: ?ConstValueNode, deprecationReason?: ?string, extensions?: ?ReadOnlyObjMapLike, astNode?: ?InputValueDefinitionNode, diff --git a/src/type/index.d.ts b/src/type/index.d.ts index 365256dfd1d..cd919929ad9 100644 --- a/src/type/index.d.ts +++ b/src/type/index.d.ts @@ -82,6 +82,7 @@ export { GraphQLArgumentConfig, GraphQLArgumentExtensions, GraphQLInputValue, + GraphQLDefaultValueUsage, GraphQLInputValueConfig, GraphQLEnumTypeConfig, GraphQLEnumTypeExtensions, @@ -117,6 +118,11 @@ export { GraphQLScalarLiteralParser, } from './definition'; +export { + getCoercedDefaultValue, + getLiteralDefaultValue, +} from './defaultValues'; + export { // Predicate isDirective, diff --git a/src/type/index.js b/src/type/index.js index 6bf0943f228..af5d0b553c9 100644 --- a/src/type/index.js +++ b/src/type/index.js @@ -136,6 +136,7 @@ export type { GraphQLArgument, GraphQLArgumentConfig, GraphQLInputValue, + GraphQLDefaultValueUsage, GraphQLInputValueConfig, GraphQLEnumTypeConfig, GraphQLEnumValue, @@ -162,5 +163,10 @@ export type { GraphQLScalarLiteralParser, } from './definition'; +export { + getCoercedDefaultValue, + getLiteralDefaultValue, +} from './defaultValues'; + // Validate GraphQL schema. export { validateSchema, assertValidSchema } from './validate'; diff --git a/src/type/introspection.js b/src/type/introspection.js index f0bce5838a0..a47f0260722 100644 --- a/src/type/introspection.js +++ b/src/type/introspection.js @@ -3,7 +3,6 @@ import { invariant } from '../jsutils/invariant'; import { print } from '../language/printer'; import { DirectiveLocation } from '../language/directiveLocation'; -import { astFromValue } from '../utilities/astFromValue'; import type { GraphQLSchema } from './schema'; import type { GraphQLDirective } from './directives'; @@ -31,6 +30,7 @@ import { isNonNullType, isAbstractType, } from './definition'; +import { getLiteralDefaultValue } from './defaultValues'; export const __Schema: GraphQLObjectType = new GraphQLObjectType({ name: '__Schema', @@ -382,11 +382,15 @@ export const __InputValue: GraphQLObjectType = new GraphQLObjectType({ type: GraphQLString, description: 'A GraphQL-formatted string representing the default value for this input value.', - resolve(inputValue) { - const { type, defaultValue } = inputValue; - const valueAST = astFromValue(defaultValue, type); - return valueAST ? print(valueAST) : null; - }, + resolve: (inputValue) => + inputValue.defaultValue + ? print( + getLiteralDefaultValue( + inputValue.defaultValue, + inputValue.type, + ), + ) + : null, }, isDeprecated: { type: new GraphQLNonNull(GraphQLBoolean), diff --git a/src/utilities/TypeInfo.js b/src/utilities/TypeInfo.js index 10225d17549..2455309867e 100644 --- a/src/utilities/TypeInfo.js +++ b/src/utilities/TypeInfo.js @@ -209,7 +209,7 @@ export class TypeInfo { } } this._argument = argDef; - this._defaultValueStack.push(argDef ? argDef.defaultValue : undefined); + this._defaultValueStack.push(argDef?.defaultValue?.value); this._inputTypeStack.push(isInputType(argType) ? argType : undefined); break; } @@ -233,9 +233,7 @@ export class TypeInfo { inputFieldType = inputField.type; } } - this._defaultValueStack.push( - inputField ? inputField.defaultValue : undefined, - ); + this._defaultValueStack.push(inputField?.defaultValue?.value); this._inputTypeStack.push( isInputType(inputFieldType) ? inputFieldType : undefined, ); diff --git a/src/utilities/__tests__/astFromValue-test.js b/src/utilities/__tests__/astFromValue-test.js index 3641f00227e..99dc3fdb5ae 100644 --- a/src/utilities/__tests__/astFromValue-test.js +++ b/src/utilities/__tests__/astFromValue-test.js @@ -51,6 +51,8 @@ describe('astFromValue', () => { kind: 'BooleanValue', value: false, }); + + expect(astFromValue(null, NonNullBoolean)).to.equal(null); }); it('converts Int values to Int ASTs', () => { diff --git a/src/utilities/__tests__/buildClientSchema-test.js b/src/utilities/__tests__/buildClientSchema-test.js index bc03e3245b6..9dcf0ab0fe4 100644 --- a/src/utilities/__tests__/buildClientSchema-test.js +++ b/src/utilities/__tests__/buildClientSchema-test.js @@ -438,6 +438,7 @@ describe('Type System: build schema from introspection', () => { } type Query { + defaultID(intArg: ID = "123"): String defaultInt(intArg: Int = 30): String defaultList(listArg: [Int] = [1, 2, 3]): String defaultObject(objArg: Geo = {lat: 37.485, lon: -122.148}): String @@ -592,6 +593,27 @@ describe('Type System: build schema from introspection', () => { expect(result.data).to.deep.equal({ foo: 'bar' }); }); + it('can use client schema for execution if resolvers are added', () => { + const schema = buildSchema(` + type Query { + foo(bar: String = "abc"): String + } + `); + + const introspection = introspectionFromSchema(schema); + const clientSchema = buildClientSchema(introspection); + + const QueryType: GraphQLObjectType = (clientSchema.getType('Query'): any); + QueryType.getFields().foo.resolve = (_value, args) => args.bar; + + const result = graphqlSync({ + schema: clientSchema, + source: '{ foo }', + }); + + expect(result.data).to.deep.equal({ foo: 'abc' }); + }); + it('can build invalid schema', () => { const schema = buildSchema('type Query', { assumeValid: true }); diff --git a/src/utilities/__tests__/valueFromAST-test.js b/src/utilities/__tests__/valueFromAST-test.js index a4443927770..187472d0d60 100644 --- a/src/utilities/__tests__/valueFromAST-test.js +++ b/src/utilities/__tests__/valueFromAST-test.js @@ -188,6 +188,21 @@ describe('valueFromAST', () => { ); }); + it('uses default values for unprovided fields', () => { + const type = new GraphQLInputObjectType({ + name: 'TestInput', + fields: { + int: { type: GraphQLInt, defaultValue: 42 }, + float: { + type: GraphQLFloat, + defaultValueLiteral: { kind: 'FloatValue', value: '3.14' }, + }, + }, + }); + + expectValueFrom('{}', type).to.deep.equal({ int: 42, float: 3.14 }); + }); + const testInputObj = new GraphQLInputObjectType({ name: 'TestInput', fields: { diff --git a/src/utilities/astFromValue.js b/src/utilities/astFromValue.js index 5621659f73a..af9b9c74a6d 100644 --- a/src/utilities/astFromValue.js +++ b/src/utilities/astFromValue.js @@ -3,7 +3,7 @@ import { invariant } from '../jsutils/invariant'; import { isObjectLike } from '../jsutils/isObjectLike'; import { isIterableObject } from '../jsutils/isIterableObject'; -import type { ValueNode } from '../language/ast'; +import type { ConstValueNode } from '../language/ast'; import { Kind } from '../language/kinds'; import type { GraphQLInputType } from '../type/definition'; @@ -37,13 +37,15 @@ import { * | null | NullValue | * */ -export function astFromValue(value: mixed, type: GraphQLInputType): ?ValueNode { +export function astFromValue( + value: mixed, + type: GraphQLInputType, +): ?ConstValueNode { if (isNonNullType(type)) { - const astValue = astFromValue(value, type.ofType); - if (astValue?.kind === Kind.NULL) { + if (value === null) { return null; } - return astValue; + return astFromValue(value, type.ofType); } // only explicit null, not undefined, NaN diff --git a/src/utilities/buildClientSchema.js b/src/utilities/buildClientSchema.js index 487ee6d16e2..29daeb282cb 100644 --- a/src/utilities/buildClientSchema.js +++ b/src/utilities/buildClientSchema.js @@ -3,7 +3,7 @@ import { devAssert } from '../jsutils/devAssert'; import { keyValMap } from '../jsutils/keyValMap'; import { isObjectLike } from '../jsutils/isObjectLike'; -import { parseValue } from '../language/parser'; +import { parseConstValue } from '../language/parser'; import type { GraphQLSchemaValidationOptions } from '../type/schema'; import type { @@ -47,7 +47,6 @@ import type { IntrospectionTypeRef, IntrospectionNamedTypeRef, } from './getIntrospectionQuery'; -import { valueFromAST } from './valueFromAST'; /** * Build a GraphQLSchema for use by client tools. @@ -367,14 +366,13 @@ export function buildClientSchema( ); } - const defaultValue = - inputValueIntrospection.defaultValue != null - ? valueFromAST(parseValue(inputValueIntrospection.defaultValue), type) - : undefined; return { description: inputValueIntrospection.description, type, - defaultValue, + defaultValueLiteral: + inputValueIntrospection.defaultValue != null + ? parseConstValue(inputValueIntrospection.defaultValue) + : undefined, deprecationReason: inputValueIntrospection.deprecationReason, }; } diff --git a/src/utilities/coerceInputValue.js b/src/utilities/coerceInputValue.js index 9c9546fbc2e..31896064f50 100644 --- a/src/utilities/coerceInputValue.js +++ b/src/utilities/coerceInputValue.js @@ -17,6 +17,7 @@ import { isListType, isNonNullType, } from '../type/definition'; +import { getCoercedDefaultValue } from '../type/defaultValues'; type OnErrorCB = ( path: $ReadOnlyArray, @@ -102,8 +103,11 @@ function coerceInputValueImpl( const fieldValue = inputValue[field.name]; if (fieldValue === undefined) { - if (field.defaultValue !== undefined) { - coercedValue[field.name] = field.defaultValue; + if (field.defaultValue) { + coercedValue[field.name] = getCoercedDefaultValue( + field.defaultValue, + field.type, + ); } else if (isNonNullType(field.type)) { const typeStr = inspect(field.type); onError( diff --git a/src/utilities/extendSchema.js b/src/utilities/extendSchema.js index a824f0fb97f..23c85774fa1 100644 --- a/src/utilities/extendSchema.js +++ b/src/utilities/extendSchema.js @@ -80,8 +80,6 @@ import { GraphQLInputObjectType, } from '../type/definition'; -import { valueFromAST } from './valueFromAST'; - type Options = {| ...GraphQLSchemaValidationOptions, @@ -496,7 +494,7 @@ export function extendSchemaImpl( configMap[node.name.value] = { type, description: node.description?.value, - defaultValue: valueFromAST(node.defaultValue, type), + defaultValueLiteral: node.defaultValue, deprecationReason: getDeprecationReason(node), astNode: node, }; diff --git a/src/utilities/findBreakingChanges.js b/src/utilities/findBreakingChanges.js index 08caec042e7..8e70b9a23ec 100644 --- a/src/utilities/findBreakingChanges.js +++ b/src/utilities/findBreakingChanges.js @@ -3,6 +3,7 @@ import { inspect } from '../jsutils/inspect'; import { invariant } from '../jsutils/invariant'; import { naturalCompare } from '../jsutils/naturalCompare'; +import type { ConstValueNode } from '../language/ast'; import { print } from '../language/printer'; import { visit } from '../language/visitor'; @@ -10,7 +11,6 @@ import type { GraphQLSchema } from '../type/schema'; import type { GraphQLField, GraphQLType, - GraphQLInputType, GraphQLNamedType, GraphQLEnumType, GraphQLUnionType, @@ -31,8 +31,7 @@ import { isNamedType, isRequiredInput, } from '../type/definition'; - -import { astFromValue } from './astFromValue'; +import { getLiteralDefaultValue } from '../type/defaultValues'; export const BreakingChangeType = Object.freeze({ TYPE_REMOVED: 'TYPE_REMOVED', @@ -402,18 +401,23 @@ function findArgChanges( `${oldType.name}.${oldField.name} arg ${oldArg.name} has changed type from ` + `${String(oldArg.type)} to ${String(newArg.type)}.`, }); - } else if (oldArg.defaultValue !== undefined) { - if (newArg.defaultValue === undefined) { + } else if (oldArg.defaultValue) { + if (!newArg.defaultValue) { schemaChanges.push({ type: DangerousChangeType.ARG_DEFAULT_VALUE_CHANGE, description: `${oldType.name}.${oldField.name} arg ${oldArg.name} defaultValue was removed.`, }); } else { + const newArgDefaultValue = newArg.defaultValue; // Since we looking only for client's observable changes we should // compare default values in the same representation as they are // represented inside introspection. - const oldValueStr = stringifyValue(oldArg.defaultValue, oldArg.type); - const newValueStr = stringifyValue(newArg.defaultValue, newArg.type); + const oldValueStr = stringifyValue( + getLiteralDefaultValue(oldArg.defaultValue, oldArg.type), + ); + const newValueStr = stringifyValue( + getLiteralDefaultValue(newArgDefaultValue, newArg.type), + ); if (oldValueStr !== newValueStr) { schemaChanges.push({ @@ -533,10 +537,7 @@ function typeKindName(type: GraphQLNamedType): string { invariant(false, 'Unexpected type: ' + inspect((type: empty))); } -function stringifyValue(value: mixed, type: GraphQLInputType): string { - const ast = astFromValue(value, type); - invariant(ast != null); - +function stringifyValue(ast: ConstValueNode): string { const sortedAST = visit(ast, { ObjectValue(objectNode) { // Make a copy since sort mutates array diff --git a/src/utilities/printSchema.js b/src/utilities/printSchema.js index cc41725368a..0e7e8cfaade 100644 --- a/src/utilities/printSchema.js +++ b/src/utilities/printSchema.js @@ -31,8 +31,7 @@ import { isEnumType, isInputObjectType, } from '../type/definition'; - -import { astFromValue } from './astFromValue'; +import { getLiteralDefaultValue } from '../type/defaultValues'; export function printSchema(schema: GraphQLSchema): string { return printFilteredSchema( @@ -258,10 +257,11 @@ function printArgs( } function printInputValue(arg: GraphQLInputField): string { - const defaultAST = astFromValue(arg.defaultValue, arg.type); let argDecl = arg.name + ': ' + String(arg.type); - if (defaultAST) { - argDecl += ` = ${print(defaultAST)}`; + if (arg.defaultValue) { + argDecl += ` = ${print( + getLiteralDefaultValue(arg.defaultValue, arg.type), + )}`; } return argDecl + printDeprecated(arg.deprecationReason); } diff --git a/src/utilities/valueFromAST.js b/src/utilities/valueFromAST.js index 258976462b9..19081ea96e0 100644 --- a/src/utilities/valueFromAST.js +++ b/src/utilities/valueFromAST.js @@ -111,8 +111,13 @@ export function valueFromAST( for (const field of Object.values(type.getFields())) { const fieldNode = fieldNodes[field.name]; if (!fieldNode || isMissingVariable(fieldNode.value, variables)) { - if (field.defaultValue !== undefined) { - coercedObj[field.name] = field.defaultValue; + if (field.defaultValue) { + // NOTE: This is an inlined version of "getCoercedDefaultValue" which + // cannot be used directly since it would cause a circular dependency. + coercedObj[field.name] = + field.defaultValue.value !== undefined + ? field.defaultValue.value + : valueFromAST(field.defaultValue.literal, field.type); } else if (isNonNullType(field.type)) { return; // Invalid: intentionally return no value. }