Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 7a927c5

Browse files
leebyronyaacovCR
authored andcommittedFeb 6, 2023
Preserve defaultValue literals
Fixes #3051 This change solves the problem of default values defined via SDL not always resolving correctly through introspection by preserving the original GraphQL literal in the schema definition. This changes argument and input field definitions `defaultValue` field from just the "value" to a new `GraphQLDefaultValueUsage` type which contains either or both "value" and "literal" fields. Since either of these fields may be set, new functions for resolving a value or literal from either have been added - `getLiteralDefaultValue` and `getCoercedDefaultValue` - these replace uses that either assumed a set value or were manually converting a value back to a literal. Here is the flow for how a default value defined in an SDL would be converted into a functional schema and back to an SDL: **Before this change:** ``` (SDL) --parse-> (AST) --coerceInputLiteral--> (defaultValue config) --valueToAST--> (AST) --print --> (SDL) ``` `coerceInputLiteral` performs coercion which is a one-way function, and `valueToAST` is unsafe and set to be deprecated in #3049. **After this change:** ``` (SDL) --parse-> (defaultValue literal config) --print --> (SDL) ```
1 parent 2fc8629 commit 7a927c5

17 files changed

+233
-49
lines changed
 

‎src/execution/values.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import type { GraphQLDirective } from '../type/directives.js';
2020
import type { GraphQLSchema } from '../type/schema.js';
2121

2222
import {
23+
coerceDefaultValue,
2324
coerceInputLiteral,
2425
coerceInputValue,
2526
} from '../utilities/coerceInputValue.js';
@@ -170,8 +171,11 @@ export function getArgumentValues(
170171
const argumentNode = argNodeMap[name];
171172

172173
if (!argumentNode) {
173-
if (argDef.defaultValue !== undefined) {
174-
coercedValues[name] = argDef.defaultValue;
174+
if (argDef.defaultValue) {
175+
coercedValues[name] = coerceDefaultValue(
176+
argDef.defaultValue,
177+
argDef.type,
178+
);
175179
} else if (isNonNullType(argType)) {
176180
throw new GraphQLError(
177181
`Argument ${argDef} of required type ${argType} was not provided.`,
@@ -190,8 +194,11 @@ export function getArgumentValues(
190194
variableValues == null ||
191195
!hasOwnProperty(variableValues, variableName)
192196
) {
193-
if (argDef.defaultValue !== undefined) {
194-
coercedValues[name] = argDef.defaultValue;
197+
if (argDef.defaultValue) {
198+
coercedValues[name] = coerceDefaultValue(
199+
argDef.defaultValue,
200+
argDef.type,
201+
);
195202
} else if (isNonNullType(argType)) {
196203
throw new GraphQLError(
197204
`Argument ${argDef} of required type ${argType} ` +

‎src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ export type {
199199
GraphQLScalarSerializer,
200200
GraphQLScalarValueParser,
201201
GraphQLScalarLiteralParser,
202+
GraphQLDefaultValueUsage,
202203
} from './type/index.js';
203204

204205
// Parse and operate on GraphQL language source files.

‎src/type/__tests__/definition-test.ts

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { describe, it } from 'mocha';
44
import { identityFunc } from '../../jsutils/identityFunc.js';
55
import { inspect } from '../../jsutils/inspect.js';
66

7+
import { Kind } from '../../language/kinds.js';
78
import { parseValue } from '../../language/parser.js';
89

910
import type { GraphQLNullableType, GraphQLType } from '../definition.js';
@@ -617,6 +618,65 @@ describe('Type System: Input Objects', () => {
617618
'not used anymore',
618619
);
619620
});
621+
622+
describe('Input Object fields may have default values', () => {
623+
it('accepts an Input Object type with a default value', () => {
624+
const inputObjType = new GraphQLInputObjectType({
625+
name: 'SomeInputObject',
626+
fields: {
627+
f: { type: ScalarType, defaultValue: 3 },
628+
},
629+
});
630+
expect(inputObjType.getFields().f).to.deep.include({
631+
coordinate: 'SomeInputObject.f',
632+
name: 'f',
633+
description: undefined,
634+
type: ScalarType,
635+
defaultValue: { value: 3 },
636+
deprecationReason: undefined,
637+
extensions: {},
638+
astNode: undefined,
639+
});
640+
});
641+
642+
it('accepts an Input Object type with a default value literal', () => {
643+
const inputObjType = new GraphQLInputObjectType({
644+
name: 'SomeInputObject',
645+
fields: {
646+
f: {
647+
type: ScalarType,
648+
defaultValueLiteral: { kind: Kind.INT, value: '3' },
649+
},
650+
},
651+
});
652+
expect(inputObjType.getFields().f).to.deep.include({
653+
coordinate: 'SomeInputObject.f',
654+
name: 'f',
655+
description: undefined,
656+
type: ScalarType,
657+
defaultValue: { literal: { kind: 'IntValue', value: '3' } },
658+
deprecationReason: undefined,
659+
extensions: {},
660+
astNode: undefined,
661+
});
662+
});
663+
664+
it('rejects an Input Object type with potentially conflicting default values', () => {
665+
const inputObjType = new GraphQLInputObjectType({
666+
name: 'SomeInputObject',
667+
fields: {
668+
f: {
669+
type: ScalarType,
670+
defaultValue: 3,
671+
defaultValueLiteral: { kind: Kind.INT, value: '3' },
672+
},
673+
},
674+
});
675+
expect(() => inputObjType.getFields()).to.throw(
676+
'f has both a defaultValue and a defaultValueLiteral property, but only one must be provided.',
677+
);
678+
});
679+
});
620680
});
621681

622682
describe('Type System: List', () => {

‎src/type/definition.ts

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import { toObjMap } from '../jsutils/toObjMap.js';
1616
import { GraphQLError } from '../error/GraphQLError.js';
1717

1818
import type {
19+
ConstValueNode,
1920
EnumTypeDefinitionNode,
2021
EnumTypeExtensionNode,
2122
EnumValueDefinitionNode,
@@ -908,6 +909,7 @@ export interface GraphQLArgumentConfig {
908909
description?: Maybe<string>;
909910
type: GraphQLInputType;
910911
defaultValue?: unknown;
912+
defaultValueLiteral?: ConstValueNode | undefined;
911913
deprecationReason?: Maybe<string>;
912914
extensions?: Maybe<Readonly<GraphQLArgumentExtensions>>;
913915
astNode?: Maybe<InputValueDefinitionNode>;
@@ -984,7 +986,7 @@ export class GraphQLArgument extends GraphQLSchemaElement {
984986
name: string;
985987
description: Maybe<string>;
986988
type: GraphQLInputType;
987-
defaultValue: unknown;
989+
defaultValue: GraphQLDefaultValueUsage | undefined;
988990
deprecationReason: Maybe<string>;
989991
extensions: Readonly<GraphQLArgumentExtensions>;
990992
astNode: Maybe<InputValueDefinitionNode>;
@@ -999,7 +1001,7 @@ export class GraphQLArgument extends GraphQLSchemaElement {
9991001
this.name = assertName(name);
10001002
this.description = config.description;
10011003
this.type = config.type;
1002-
this.defaultValue = config.defaultValue;
1004+
this.defaultValue = defineDefaultValue(coordinate, config);
10031005
this.deprecationReason = config.deprecationReason;
10041006
this.extensions = toObjMap(config.extensions);
10051007
this.astNode = config.astNode;
@@ -1013,7 +1015,8 @@ export class GraphQLArgument extends GraphQLSchemaElement {
10131015
return {
10141016
description: this.description,
10151017
type: this.type,
1016-
defaultValue: this.defaultValue,
1018+
defaultValue: this.defaultValue?.value,
1019+
defaultValueLiteral: this.defaultValue?.literal,
10171020
deprecationReason: this.deprecationReason,
10181021
extensions: this.extensions,
10191022
astNode: this.astNode,
@@ -1029,6 +1032,26 @@ export type GraphQLFieldMap<TSource, TContext> = ObjMap<
10291032
GraphQLField<TSource, TContext>
10301033
>;
10311034

1035+
export type GraphQLDefaultValueUsage =
1036+
| { value: unknown; literal?: never }
1037+
| { literal: ConstValueNode; value?: never };
1038+
1039+
function defineDefaultValue(
1040+
coordinate: string,
1041+
config: GraphQLArgumentConfig | GraphQLInputFieldConfig,
1042+
): GraphQLDefaultValueUsage | undefined {
1043+
if (config.defaultValue === undefined && !config.defaultValueLiteral) {
1044+
return;
1045+
}
1046+
devAssert(
1047+
!(config.defaultValue !== undefined && config.defaultValueLiteral),
1048+
`${coordinate} has both a defaultValue and a defaultValueLiteral property, but only one must be provided.`,
1049+
);
1050+
return config.defaultValueLiteral
1051+
? { literal: config.defaultValueLiteral }
1052+
: { value: config.defaultValue };
1053+
}
1054+
10321055
/**
10331056
* Custom extensions
10341057
*
@@ -1608,6 +1631,7 @@ export interface GraphQLInputFieldConfig {
16081631
description?: Maybe<string>;
16091632
type: GraphQLInputType;
16101633
defaultValue?: unknown;
1634+
defaultValueLiteral?: ConstValueNode | undefined;
16111635
deprecationReason?: Maybe<string>;
16121636
extensions?: Maybe<Readonly<GraphQLInputFieldExtensions>>;
16131637
astNode?: Maybe<InputValueDefinitionNode>;
@@ -1619,7 +1643,7 @@ export class GraphQLInputField extends GraphQLSchemaElement {
16191643
name: string;
16201644
description: Maybe<string>;
16211645
type: GraphQLInputType;
1622-
defaultValue: unknown;
1646+
defaultValue: GraphQLDefaultValueUsage | undefined;
16231647
deprecationReason: Maybe<string>;
16241648
extensions: Readonly<GraphQLInputFieldExtensions>;
16251649
astNode: Maybe<InputValueDefinitionNode>;
@@ -1640,7 +1664,7 @@ export class GraphQLInputField extends GraphQLSchemaElement {
16401664
this.name = assertName(name);
16411665
this.description = config.description;
16421666
this.type = config.type;
1643-
this.defaultValue = config.defaultValue;
1667+
this.defaultValue = defineDefaultValue(coordinate, config);
16441668
this.deprecationReason = config.deprecationReason;
16451669
this.extensions = toObjMap(config.extensions);
16461670
this.astNode = config.astNode;
@@ -1654,7 +1678,8 @@ export class GraphQLInputField extends GraphQLSchemaElement {
16541678
return {
16551679
description: this.description,
16561680
type: this.type,
1657-
defaultValue: this.defaultValue,
1681+
defaultValue: this.defaultValue?.value,
1682+
defaultValueLiteral: this.defaultValue?.literal,
16581683
deprecationReason: this.deprecationReason,
16591684
extensions: this.extensions,
16601685
astNode: this.astNode,

‎src/type/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ export type {
120120
GraphQLScalarSerializer,
121121
GraphQLScalarValueParser,
122122
GraphQLScalarLiteralParser,
123+
GraphQLDefaultValueUsage,
123124
} from './definition.js';
124125

125126
export {

‎src/type/introspection.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,8 +395,13 @@ export const __InputValue: GraphQLObjectType = new GraphQLObjectType({
395395
'A GraphQL-formatted string representing the default value for this input value.',
396396
resolve(inputValue) {
397397
const { type, defaultValue } = inputValue;
398-
const valueAST = astFromValue(defaultValue, type);
399-
return valueAST ? print(valueAST) : null;
398+
if (!defaultValue) {
399+
return null;
400+
}
401+
const literal =
402+
defaultValue.literal ?? astFromValue(defaultValue.value, type);
403+
invariant(literal != null, 'Invalid default value');
404+
return print(literal);
400405
},
401406
},
402407
isDeprecated: {

‎src/utilities/TypeInfo.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { getEnterLeaveForKind } from '../language/visitor.js';
99
import type {
1010
GraphQLArgument,
1111
GraphQLCompositeType,
12+
GraphQLDefaultValueUsage,
1213
GraphQLEnumValue,
1314
GraphQLField,
1415
GraphQLInputField,
@@ -43,7 +44,7 @@ export class TypeInfo {
4344
private _parentTypeStack: Array<Maybe<GraphQLCompositeType>>;
4445
private _inputTypeStack: Array<Maybe<GraphQLInputType>>;
4546
private _fieldDefStack: Array<Maybe<GraphQLField<unknown, unknown>>>;
46-
private _defaultValueStack: Array<Maybe<unknown>>;
47+
private _defaultValueStack: Array<GraphQLDefaultValueUsage | undefined>;
4748
private _directive: Maybe<GraphQLDirective>;
4849
private _argument: Maybe<GraphQLArgument>;
4950
private _enumValue: Maybe<GraphQLEnumValue>;
@@ -117,7 +118,7 @@ export class TypeInfo {
117118
}
118119
}
119120

120-
getDefaultValue(): Maybe<unknown> {
121+
getDefaultValue(): GraphQLDefaultValueUsage | undefined {
121122
if (this._defaultValueStack.length > 0) {
122123
return this._defaultValueStack[this._defaultValueStack.length - 1];
123124
}

‎src/utilities/__tests__/buildClientSchema-test.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ describe('Type System: build schema from introspection', () => {
445445
}
446446
447447
type Query {
448+
defaultID(intArg: ID = "123"): String
448449
defaultInt(intArg: Int = 30): String
449450
defaultList(listArg: [Int] = [1, 2, 3]): String
450451
defaultObject(objArg: Geo = { lat: 37.485, lon: -122.148 }): String
@@ -600,6 +601,28 @@ describe('Type System: build schema from introspection', () => {
600601
expect(result.data).to.deep.equal({ foo: 'bar' });
601602
});
602603

604+
it('can use client schema for execution if resolvers are added', () => {
605+
const schema = buildSchema(`
606+
type Query {
607+
foo(bar: String = "abc"): String
608+
}
609+
`);
610+
611+
const introspection = introspectionFromSchema(schema);
612+
const clientSchema = buildClientSchema(introspection);
613+
614+
const QueryType: GraphQLObjectType = clientSchema.getType('Query') as any;
615+
QueryType.getFields().foo.resolve = (_value, args) => args.bar;
616+
617+
const result = graphqlSync({
618+
schema: clientSchema,
619+
source: '{ foo }',
620+
});
621+
622+
expect(result.data).to.deep.equal({ foo: 'abc' });
623+
expect(result.data).to.deep.equal({ foo: 'abc' });
624+
});
625+
603626
it('can build invalid schema', () => {
604627
const schema = buildSchema('type Query', { assumeValid: true });
605628

‎src/utilities/__tests__/coerceInputValue-test.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { identityFunc } from '../../jsutils/identityFunc.js';
55
import { invariant } from '../../jsutils/invariant.js';
66
import type { ObjMap } from '../../jsutils/ObjMap.js';
77

8+
import { Kind } from '../../language/kinds.js';
89
import { parseValue } from '../../language/parser.js';
910
import { print } from '../../language/printer.js';
1011

@@ -24,7 +25,11 @@ import {
2425
GraphQLString,
2526
} from '../../type/scalars.js';
2627

27-
import { coerceInputLiteral, coerceInputValue } from '../coerceInputValue.js';
28+
import {
29+
coerceDefaultValue,
30+
coerceInputLiteral,
31+
coerceInputValue,
32+
} from '../coerceInputValue.js';
2833

2934
interface CoerceResult {
3035
value: unknown;
@@ -610,10 +615,14 @@ describe('coerceInputLiteral', () => {
610615
name: 'TestInput',
611616
fields: {
612617
int: { type: GraphQLInt, defaultValue: 42 },
618+
float: {
619+
type: GraphQLFloat,
620+
defaultValueLiteral: { kind: Kind.FLOAT, value: '3.14' },
621+
},
613622
},
614623
});
615624

616-
test('{}', type, { int: 42 });
625+
test('{}', type, { int: 42, float: 3.14 });
617626
});
618627

619628
const testInputObj = new GraphQLInputObjectType({
@@ -681,3 +690,26 @@ describe('coerceInputLiteral', () => {
681690
});
682691
});
683692
});
693+
694+
describe('coerceDefaultValue', () => {
695+
it('memoizes coercion', () => {
696+
const parseValueCalls: any = [];
697+
698+
const spyScalar = new GraphQLScalarType({
699+
name: 'SpyScalar',
700+
parseValue(value) {
701+
parseValueCalls.push(value);
702+
return value;
703+
},
704+
});
705+
706+
const defaultValueUsage = {
707+
literal: { kind: Kind.STRING, value: 'hello' },
708+
} as const;
709+
expect(coerceDefaultValue(defaultValueUsage, spyScalar)).to.equal('hello');
710+
711+
// Call a second time
712+
expect(coerceDefaultValue(defaultValueUsage, spyScalar)).to.equal('hello');
713+
expect(parseValueCalls).to.deep.equal(['hello']);
714+
});
715+
});

‎src/utilities/astFromValue.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { isIterableObject } from '../jsutils/isIterableObject.js';
44
import { isObjectLike } from '../jsutils/isObjectLike.js';
55
import type { Maybe } from '../jsutils/Maybe.js';
66

7-
import type { ObjectFieldNode, ValueNode } from '../language/ast.js';
7+
import type { ConstObjectFieldNode, ConstValueNode } from '../language/ast.js';
88
import { Kind } from '../language/kinds.js';
99

1010
import type { GraphQLInputType } from '../type/definition.js';
@@ -41,7 +41,7 @@ import { GraphQLID } from '../type/scalars.js';
4141
export function astFromValue(
4242
value: unknown,
4343
type: GraphQLInputType,
44-
): Maybe<ValueNode> {
44+
): Maybe<ConstValueNode> {
4545
if (isNonNullType(type)) {
4646
const astValue = astFromValue(value, type.ofType);
4747
if (astValue?.kind === Kind.NULL) {
@@ -83,7 +83,7 @@ export function astFromValue(
8383
if (!isObjectLike(value)) {
8484
return null;
8585
}
86-
const fieldNodes: Array<ObjectFieldNode> = [];
86+
const fieldNodes: Array<ConstObjectFieldNode> = [];
8787
for (const field of Object.values(type.getFields())) {
8888
const fieldValue = astFromValue(value[field.name], field.type);
8989
if (fieldValue) {

‎src/utilities/buildClientSchema.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ import { specifiedScalarTypes } from '../type/scalars.js';
3232
import type { GraphQLSchemaValidationOptions } from '../type/schema.js';
3333
import { GraphQLSchema } from '../type/schema.js';
3434

35-
import { coerceInputLiteral } from './coerceInputValue.js';
3635
import type {
3736
IntrospectionDirective,
3837
IntrospectionEnumType,
@@ -370,17 +369,13 @@ export function buildClientSchema(
370369
);
371370
}
372371

373-
const defaultValue =
374-
inputValueIntrospection.defaultValue != null
375-
? coerceInputLiteral(
376-
parseConstValue(inputValueIntrospection.defaultValue),
377-
type,
378-
)
379-
: undefined;
380372
return {
381373
description: inputValueIntrospection.description,
382374
type,
383-
defaultValue,
375+
defaultValueLiteral:
376+
inputValueIntrospection.defaultValue != null
377+
? parseConstValue(inputValueIntrospection.defaultValue)
378+
: undefined,
384379
deprecationReason: inputValueIntrospection.deprecationReason,
385380
};
386381
}

‎src/utilities/coerceInputValue.ts

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ import { GraphQLError } from '../error/GraphQLError.js';
1717
import type { ValueNode } from '../language/ast';
1818
import { Kind } from '../language/kinds.js';
1919

20-
import type { GraphQLInputType } from '../type/definition.js';
20+
import type {
21+
GraphQLDefaultValueUsage,
22+
GraphQLInputType,
23+
} from '../type/definition';
2124
import {
2225
assertLeafType,
2326
isInputObjectType,
@@ -111,8 +114,11 @@ function coerceInputValueImpl(
111114
const fieldValue = inputValue[field.name];
112115

113116
if (fieldValue === undefined) {
114-
if (field.defaultValue !== undefined) {
115-
coercedValue[field.name] = field.defaultValue;
117+
if (field.defaultValue) {
118+
coercedValue[field.name] = coerceDefaultValue(
119+
field.defaultValue,
120+
field.type,
121+
);
116122
} else if (isNonNullType(field.type)) {
117123
const typeStr = inspect(field.type);
118124
onError(
@@ -273,8 +279,11 @@ export function coerceInputLiteral(
273279
if (isRequiredInputField(field)) {
274280
return; // Invalid: intentionally return no value.
275281
}
276-
if (field.defaultValue !== undefined) {
277-
coercedValue[field.name] = field.defaultValue;
282+
if (field.defaultValue) {
283+
coercedValue[field.name] = coerceDefaultValue(
284+
field.defaultValue,
285+
field.type,
286+
);
278287
}
279288
} else {
280289
const fieldValue = coerceInputLiteral(
@@ -311,3 +320,23 @@ function isMissingVariable(
311320
(variables == null || !hasOwnProperty(variables, valueNode.name.value))
312321
);
313322
}
323+
324+
/**
325+
* @internal
326+
*/
327+
export function coerceDefaultValue(
328+
defaultValue: GraphQLDefaultValueUsage,
329+
type: GraphQLInputType,
330+
): unknown {
331+
// Memoize the result of coercing the default value in a hidden field.
332+
let coercedValue = (defaultValue as any)._memoizedCoercedValue;
333+
// istanbul ignore else (memoized case)
334+
if (coercedValue === undefined) {
335+
coercedValue = defaultValue.literal
336+
? coerceInputLiteral(defaultValue.literal, type)
337+
: defaultValue.value;
338+
invariant(coercedValue !== undefined);
339+
(defaultValue as any)._memoizedCoercedValue = coercedValue;
340+
}
341+
return coercedValue;
342+
}

‎src/utilities/extendSchema.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,8 +83,6 @@ import { assertValidSDLExtension } from '../validation/validate.js';
8383

8484
import { getDirectiveValues } from '../execution/values.js';
8585

86-
import { coerceInputLiteral } from './coerceInputValue.js';
87-
8886
interface Options extends GraphQLSchemaValidationOptions {
8987
/**
9088
* Set to true to assume the SDL is valid.
@@ -536,9 +534,7 @@ export function extendSchemaImpl(
536534
argConfigMap[arg.name.value] = {
537535
type,
538536
description: arg.description?.value,
539-
defaultValue: arg.defaultValue
540-
? coerceInputLiteral(arg.defaultValue, type)
541-
: undefined,
537+
defaultValueLiteral: arg.defaultValue,
542538
deprecationReason: getDeprecationReason(arg),
543539
astNode: arg,
544540
};
@@ -565,9 +561,7 @@ export function extendSchemaImpl(
565561
inputFieldMap[field.name.value] = {
566562
type,
567563
description: field.description?.value,
568-
defaultValue: field.defaultValue
569-
? coerceInputLiteral(field.defaultValue, type)
570-
: undefined,
564+
defaultValueLiteral: field.defaultValue,
571565
deprecationReason: getDeprecationReason(field),
572566
astNode: field,
573567
};

‎src/utilities/findBreakingChanges.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { keyMap } from '../jsutils/keyMap.js';
55
import { print } from '../language/printer.js';
66

77
import type {
8+
GraphQLDefaultValueUsage,
89
GraphQLEnumType,
910
GraphQLField,
1011
GraphQLInputObjectType,
@@ -525,8 +526,11 @@ function typeKindName(type: GraphQLNamedType): string {
525526
invariant(false, 'Unexpected type: ' + inspect(type));
526527
}
527528

528-
function stringifyValue(value: unknown, type: GraphQLInputType): string {
529-
const ast = astFromValue(value, type);
529+
function stringifyValue(
530+
defaultValue: GraphQLDefaultValueUsage,
531+
type: GraphQLInputType,
532+
): string {
533+
const ast = defaultValue.literal ?? astFromValue(defaultValue.value, type);
530534
invariant(ast != null);
531535
return print(sortValueNode(ast));
532536
}

‎src/utilities/printSchema.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -261,10 +261,13 @@ function printArgs(
261261
}
262262

263263
function printInputValue(arg: GraphQLInputField): string {
264-
const defaultAST = astFromValue(arg.defaultValue, arg.type);
265264
let argDecl = arg.name + ': ' + String(arg.type);
266-
if (defaultAST) {
267-
argDecl += ` = ${print(defaultAST)}`;
265+
if (arg.defaultValue) {
266+
const literal =
267+
arg.defaultValue.literal ??
268+
astFromValue(arg.defaultValue.value, arg.type);
269+
invariant(literal != null, 'Invalid default value');
270+
argDecl += ` = ${print(literal)}`;
268271
}
269272
return argDecl + printDeprecated(arg.deprecationReason);
270273
}

‎src/validation/ValidationContext.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import { visit } from '../language/visitor.js';
1818
import type {
1919
GraphQLArgument,
2020
GraphQLCompositeType,
21+
GraphQLDefaultValueUsage,
2122
GraphQLEnumValue,
2223
GraphQLField,
2324
GraphQLInputType,
@@ -32,7 +33,7 @@ type NodeWithSelectionSet = OperationDefinitionNode | FragmentDefinitionNode;
3233
interface VariableUsage {
3334
readonly node: VariableNode;
3435
readonly type: Maybe<GraphQLInputType>;
35-
readonly defaultValue: Maybe<unknown>;
36+
readonly defaultValue: GraphQLDefaultValueUsage | undefined;
3637
}
3738

3839
/**

‎src/validation/rules/VariablesInAllowedPositionRule.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ import type { ValueNode } from '../../language/ast.js';
66
import { Kind } from '../../language/kinds.js';
77
import type { ASTVisitor } from '../../language/visitor.js';
88

9-
import type { GraphQLType } from '../../type/definition.js';
9+
import type {
10+
GraphQLDefaultValueUsage,
11+
GraphQLType,
12+
} from '../../type/definition.js';
1013
import { isNonNullType } from '../../type/definition.js';
1114
import type { GraphQLSchema } from '../../type/schema.js';
1215

@@ -83,7 +86,7 @@ function allowedVariableUsage(
8386
varType: GraphQLType,
8487
varDefaultValue: Maybe<ValueNode>,
8588
locationType: GraphQLType,
86-
locationDefaultValue: Maybe<unknown>,
89+
locationDefaultValue: GraphQLDefaultValueUsage | undefined,
8790
): boolean {
8891
if (isNonNullType(locationType) && !isNonNullType(varType)) {
8992
const hasNonNullVariableDefaultValue =

0 commit comments

Comments
 (0)
Please sign in to comment.