Skip to content

Commit 5686d3e

Browse files
committed
Preserve defaultValue literals
Fixes #3051
1 parent 5e2b6d5 commit 5686d3e

25 files changed

+275
-81
lines changed

src/execution/values.js

+11-4
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { typeFromAST } from '../utilities/typeFromAST';
2222
import {
2323
coerceInputValue,
2424
coerceInputLiteral,
25+
coerceDefaultValue,
2526
} from '../utilities/coerceInputValue';
2627

2728
type CoercedVariableValues =
@@ -178,8 +179,11 @@ export function getArgumentValues(
178179
const argumentNode = argNodeMap[name];
179180

180181
if (!argumentNode) {
181-
if (argDef.defaultValue !== undefined) {
182-
coercedValues[name] = argDef.defaultValue;
182+
if (argDef.defaultValue) {
183+
coercedValues[name] = coerceDefaultValue(
184+
argDef.defaultValue,
185+
argDef.type,
186+
);
183187
} else if (isNonNullType(argType)) {
184188
throw new GraphQLError(
185189
`Argument "${name}" of required type "${inspect(argType)}" ` +
@@ -199,8 +203,11 @@ export function getArgumentValues(
199203
variableValues == null ||
200204
!hasOwnProperty(variableValues, variableName)
201205
) {
202-
if (argDef.defaultValue !== undefined) {
203-
coercedValues[name] = argDef.defaultValue;
206+
if (argDef.defaultValue) {
207+
coercedValues[name] = coerceDefaultValue(
208+
argDef.defaultValue,
209+
argDef.type,
210+
);
204211
} else if (isNonNullType(argType)) {
205212
throw new GraphQLError(
206213
`Argument "${name}" of required type "${inspect(argType)}" ` +

src/index.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,7 @@ export {
150150
GraphQLArgumentConfig,
151151
GraphQLArgumentExtensions,
152152
GraphQLInputValue,
153+
GraphQLDefaultValueUsage,
153154
GraphQLInputValueConfig,
154155
GraphQLEnumTypeConfig,
155156
GraphQLEnumTypeExtensions,

src/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ export type {
146146
GraphQLArgument,
147147
GraphQLArgumentConfig,
148148
GraphQLInputValue,
149+
GraphQLDefaultValueUsage,
149150
GraphQLInputValueConfig,
150151
GraphQLEnumTypeConfig,
151152
GraphQLEnumValue,

src/type/__tests__/definition-test.js

+61
Original file line numberDiff line numberDiff line change
@@ -818,6 +818,67 @@ describe('Type System: Input Objects', () => {
818818
);
819819
});
820820
});
821+
822+
describe('Input Object fields may have default values', () => {
823+
it('accepts an Input Object type with a default value', () => {
824+
const inputObjType = new GraphQLInputObjectType({
825+
name: 'SomeInputObject',
826+
fields: {
827+
f: { type: ScalarType, defaultValue: 3 },
828+
},
829+
});
830+
expect(inputObjType.getFields()).to.deep.equal({
831+
f: {
832+
name: 'f',
833+
description: undefined,
834+
type: ScalarType,
835+
defaultValue: { value: 3 },
836+
deprecationReason: undefined,
837+
extensions: undefined,
838+
astNode: undefined,
839+
},
840+
});
841+
});
842+
843+
it('accepts an Input Object type with a default value literal', () => {
844+
const inputObjType = new GraphQLInputObjectType({
845+
name: 'SomeInputObject',
846+
fields: {
847+
f: {
848+
type: ScalarType,
849+
defaultValueLiteral: { kind: 'IntValue', value: '3' },
850+
},
851+
},
852+
});
853+
expect(inputObjType.getFields()).to.deep.equal({
854+
f: {
855+
name: 'f',
856+
description: undefined,
857+
type: ScalarType,
858+
defaultValue: { literal: { kind: 'IntValue', value: '3' } },
859+
deprecationReason: undefined,
860+
extensions: undefined,
861+
astNode: undefined,
862+
},
863+
});
864+
});
865+
866+
it('rejects an Input Object type with potentially conflicting default values', () => {
867+
const inputObjType = new GraphQLInputObjectType({
868+
name: 'SomeInputObject',
869+
fields: {
870+
f: {
871+
type: ScalarType,
872+
defaultValue: 3,
873+
defaultValueLiteral: { kind: 'IntValue', value: '3' },
874+
},
875+
},
876+
});
877+
expect(() => inputObjType.getFields()).to.throw(
878+
'f has both a defaultValue and a defaultValueLiteral property, but only one must be provided.',
879+
);
880+
});
881+
});
821882
});
822883

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

src/type/__tests__/predicate-test.js

+11-20
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import {
6969
assertNamedType,
7070
getNullableType,
7171
getNamedType,
72+
defineInputValue,
7273
} from '../definition';
7374

7475
const ObjectType = new GraphQLObjectType({ name: 'Object', fields: {} });
@@ -562,19 +563,14 @@ describe('Type predicates', () => {
562563
});
563564

564565
describe('isRequiredInput', () => {
565-
function buildArg(config: {|
566+
function buildArg({
567+
type,
568+
defaultValue,
569+
}: {|
566570
type: GraphQLInputType,
567571
defaultValue?: mixed,
568572
|}): GraphQLArgument {
569-
return {
570-
name: 'someArg',
571-
type: config.type,
572-
description: undefined,
573-
defaultValue: config.defaultValue,
574-
deprecationReason: null,
575-
extensions: undefined,
576-
astNode: undefined,
577-
};
573+
return defineInputValue({ type, defaultValue }, 'someArg');
578574
}
579575

580576
it('returns true for required arguments', () => {
@@ -608,19 +604,14 @@ describe('Type predicates', () => {
608604
expect(isRequiredInput(optArg4)).to.equal(false);
609605
});
610606

611-
function buildInputField(config: {|
607+
function buildInputField({
608+
type,
609+
defaultValue,
610+
}: {|
612611
type: GraphQLInputType,
613612
defaultValue?: mixed,
614613
|}): GraphQLInputField {
615-
return {
616-
name: 'someInputField',
617-
type: config.type,
618-
description: undefined,
619-
defaultValue: config.defaultValue,
620-
deprecationReason: null,
621-
extensions: undefined,
622-
astNode: undefined,
623-
};
614+
return defineInputValue({ type, defaultValue }, 'someInputField');
624615
}
625616

626617
it('returns true for required input field', () => {

src/type/definition.d.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {
2323
FieldNode,
2424
FragmentDefinitionNode,
2525
ValueNode,
26+
ConstValueNode,
2627
ScalarTypeExtensionNode,
2728
UnionTypeExtensionNode,
2829
EnumTypeExtensionNode,
@@ -575,12 +576,16 @@ export interface GraphQLInputValue<Extensions> {
575576
name: string;
576577
description: Maybe<string>;
577578
type: GraphQLInputType;
578-
defaultValue: unknown;
579+
defaultValue: Maybe<GraphQLDefaultValueUsage>;
579580
deprecationReason: Maybe<string>;
580581
extensions: Maybe<Readonly<Extensions>>;
581582
astNode: Maybe<InputValueDefinitionNode>;
582583
}
583584

585+
export type GraphQLDefaultValueUsage =
586+
| { value: unknown; literal?: never }
587+
| { literal: ConstValueNode; value?: never };
588+
584589
export interface GraphQLInputValueConfig<Extensions> {
585590
description?: Maybe<string>;
586591
type: GraphQLInputType;

src/type/definition.js

+24-3
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import type {
4141
FieldNode,
4242
FragmentDefinitionNode,
4343
ValueNode,
44+
ConstValueNode,
4445
} from '../language/ast';
4546

4647
import { valueFromASTUntyped } from '../utilities/valueFromASTUntyped';
@@ -971,11 +972,21 @@ export function defineInputValue(
971972
!('resolve' in config),
972973
`${name} has a resolve property, but inputs cannot define resolvers.`,
973974
);
975+
let defaultValue;
976+
if (config.defaultValue !== undefined || config.defaultValueLiteral) {
977+
devAssert(
978+
config.defaultValue === undefined || !config.defaultValueLiteral,
979+
`${name} has both a defaultValue and a defaultValueLiteral property, but only one must be provided.`,
980+
);
981+
defaultValue = config.defaultValueLiteral
982+
? { literal: config.defaultValueLiteral }
983+
: { value: config.defaultValue };
984+
}
974985
return {
975986
name,
976987
description: config.description,
977988
type: config.type,
978-
defaultValue: config.defaultValue,
989+
defaultValue,
979990
deprecationReason: config.deprecationReason,
980991
extensions: config.extensions && toObjMap(config.extensions),
981992
astNode: config.astNode,
@@ -991,7 +1002,12 @@ export function inputValueToConfig(
9911002
return {
9921003
description: inputValue.description,
9931004
type: inputValue.type,
994-
defaultValue: inputValue.defaultValue,
1005+
defaultValue: inputValue.defaultValue?.literal
1006+
? undefined
1007+
: inputValue.defaultValue?.value,
1008+
defaultValueLiteral: inputValue.defaultValue?.literal
1009+
? inputValue.defaultValue.literal
1010+
: undefined,
9951011
deprecationReason: inputValue.deprecationReason,
9961012
extensions: inputValue.extensions,
9971013
astNode: inputValue.astNode,
@@ -1002,16 +1018,21 @@ export type GraphQLInputValue = {|
10021018
name: string,
10031019
description: ?string,
10041020
type: GraphQLInputType,
1005-
defaultValue: mixed,
1021+
defaultValue: ?GraphQLDefaultValueUsage,
10061022
deprecationReason: ?string,
10071023
extensions: ?ReadOnlyObjMap<mixed>,
10081024
astNode: ?InputValueDefinitionNode,
10091025
|};
10101026

1027+
export type GraphQLDefaultValueUsage =
1028+
| {| value: mixed |}
1029+
| {| literal: ConstValueNode |};
1030+
10111031
export type GraphQLInputValueConfig = {|
10121032
description?: ?string,
10131033
type: GraphQLInputType,
10141034
defaultValue?: mixed,
1035+
defaultValueLiteral?: ?ConstValueNode,
10151036
deprecationReason?: ?string,
10161037
extensions?: ?ReadOnlyObjMapLike<mixed>,
10171038
astNode?: ?InputValueDefinitionNode,

src/type/index.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export {
8282
GraphQLArgumentConfig,
8383
GraphQLArgumentExtensions,
8484
GraphQLInputValue,
85+
GraphQLDefaultValueUsage,
8586
GraphQLInputValueConfig,
8687
GraphQLEnumTypeConfig,
8788
GraphQLEnumTypeExtensions,

src/type/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@ export type {
136136
GraphQLArgument,
137137
GraphQLArgumentConfig,
138138
GraphQLInputValue,
139+
GraphQLDefaultValueUsage,
139140
GraphQLInputValueConfig,
140141
GraphQLEnumTypeConfig,
141142
GraphQLEnumValue,

src/type/introspection.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -384,8 +384,14 @@ export const __InputValue: GraphQLObjectType = new GraphQLObjectType({
384384
'A GraphQL-formatted string representing the default value for this input value.',
385385
resolve(inputValue) {
386386
const { type, defaultValue } = inputValue;
387-
const valueAST = astFromValue(defaultValue, type);
388-
return valueAST ? print(valueAST) : null;
387+
if (!defaultValue) {
388+
return null;
389+
}
390+
const literal = defaultValue.literal
391+
? defaultValue.literal
392+
: astFromValue(defaultValue.value, type);
393+
invariant(literal, 'Invalid default value');
394+
return print(literal);
389395
},
390396
},
391397
isDeprecated: {

src/utilities/TypeInfo.d.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
GraphQLField,
1313
GraphQLArgument,
1414
GraphQLEnumValue,
15+
GraphQLDefaultValueUsage,
1516
} from '../type/definition';
1617

1718
/**
@@ -35,7 +36,7 @@ export class TypeInfo {
3536
getInputType(): Maybe<GraphQLInputType>;
3637
getParentInputType(): Maybe<GraphQLInputType>;
3738
getFieldDef(): Maybe<GraphQLField<unknown, unknown>>;
38-
getDefaultValue(): Maybe<unknown>;
39+
getDefaultValue(): Maybe<GraphQLDefaultValueUsage>;
3940
getDirective(): Maybe<GraphQLDirective>;
4041
getArgument(): Maybe<GraphQLArgument>;
4142
getEnumValue(): Maybe<GraphQLEnumValue>;

src/utilities/TypeInfo.js

+5-6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import type {
1515
GraphQLArgument,
1616
GraphQLInputField,
1717
GraphQLEnumValue,
18+
GraphQLDefaultValueUsage,
1819
} from '../type/definition';
1920
import {
2021
isObjectType,
@@ -47,7 +48,7 @@ export class TypeInfo {
4748
_parentTypeStack: Array<?GraphQLCompositeType>;
4849
_inputTypeStack: Array<?GraphQLInputType>;
4950
_fieldDefStack: Array<?GraphQLField<mixed, mixed>>;
50-
_defaultValueStack: Array<?mixed>;
51+
_defaultValueStack: Array<?GraphQLDefaultValueUsage>;
5152
_directive: ?GraphQLDirective;
5253
_argument: ?GraphQLArgument;
5354
_enumValue: ?GraphQLEnumValue;
@@ -115,7 +116,7 @@ export class TypeInfo {
115116
}
116117
}
117118

118-
getDefaultValue(): ?mixed {
119+
getDefaultValue(): ?GraphQLDefaultValueUsage {
119120
if (this._defaultValueStack.length > 0) {
120121
return this._defaultValueStack[this._defaultValueStack.length - 1];
121122
}
@@ -209,7 +210,7 @@ export class TypeInfo {
209210
}
210211
}
211212
this._argument = argDef;
212-
this._defaultValueStack.push(argDef ? argDef.defaultValue : undefined);
213+
this._defaultValueStack.push(argDef?.defaultValue);
213214
this._inputTypeStack.push(isInputType(argType) ? argType : undefined);
214215
break;
215216
}
@@ -233,9 +234,7 @@ export class TypeInfo {
233234
inputFieldType = inputField.type;
234235
}
235236
}
236-
this._defaultValueStack.push(
237-
inputField ? inputField.defaultValue : undefined,
238-
);
237+
this._defaultValueStack.push(inputField?.defaultValue);
239238
this._inputTypeStack.push(
240239
isInputType(inputFieldType) ? inputFieldType : undefined,
241240
);

src/utilities/__tests__/astFromValue-test.js

+2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ describe('astFromValue', () => {
5151
kind: 'BooleanValue',
5252
value: false,
5353
});
54+
55+
expect(astFromValue(null, NonNullBoolean)).to.equal(null);
5456
});
5557

5658
it('converts Int values to Int ASTs', () => {

0 commit comments

Comments
 (0)