Skip to content

Commit 2a1786c

Browse files
yaacovCRhayes
andauthored
ease upgrade path for programmatic default values (#4296)
#3814 added default value validation and coercion, deprecating `astFromValue()` (which was unsafe, used `serialize()` to uncoerce input values provided in the internal format) and replacing it with a call to `valueToLiteral()` which safely operates on external values. This PR makes that change backwards compatible by reintroducing it as a new config option of `default` instead of replacing the existing option of `defaultValue`, where the type of `default` is: ```ts export type GraphQLDefaultInput = | { value: unknown; literal?: never } | { literal: ConstValueNode; value?: never }; ``` `default.value` is the external default value, while old config option of `defaultValue` is the internal value. Instead of removing it in v17, it is deprecated, and will be removed in v18. Side note: `GraphqlDefaultInput` renamed from `GraphQlDefaultValueUsage` introduced within the #3814 PR stack. Co-authored-by: Michael Hayes <[email protected]>
1 parent 5303cf7 commit 2a1786c

31 files changed

+315
-265
lines changed

integrationTests/ts/basic-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const queryType: GraphQLObjectType = new GraphQLObjectType({
1111
args: {
1212
who: {
1313
type: GraphQLString,
14-
defaultValue: 'World',
14+
default: { value: 'World' },
1515
},
1616
},
1717
resolve(_root, args: { who: string }) {

integrationTests/ts/esm.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ const queryType: GraphQLObjectType = new GraphQLObjectType({
1515
args: {
1616
who: {
1717
type: GraphQLString,
18-
defaultValue: 'World',
18+
default: { value: 'World' },
1919
},
2020
},
2121
resolve(_root, args: { who: string }) {

src/execution/__tests__/executor-test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,7 @@ describe('Execute: Handles basic execution tasks', () => {
246246
signature: {
247247
name: 'var',
248248
type: GraphQLString,
249-
defaultValue: undefined,
249+
default: undefined,
250250
},
251251
value: 'abc',
252252
},

src/execution/__tests__/variables-test.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,11 @@ const TestType = new GraphQLObjectType({
142142
}),
143143
fieldWithDefaultArgumentValue: fieldWithInputArg({
144144
type: GraphQLString,
145-
defaultValue: 'Hello World',
145+
default: { value: 'Hello World' },
146146
}),
147147
fieldWithNonNullableStringInputAndDefaultArgumentValue: fieldWithInputArg({
148148
type: new GraphQLNonNull(GraphQLString),
149-
defaultValue: 'Hello World',
149+
default: { value: 'Hello World' },
150150
}),
151151
fieldWithNestedInputObject: fieldWithInputArg({
152152
type: TestNestedInputObject,
@@ -187,7 +187,7 @@ const schema = new GraphQLSchema({
187187
type: new GraphQLNonNull(GraphQLBoolean),
188188
description: 'Skipped when true.',
189189
// default values will override operation variables in the setting of defined fragment variables that are not provided
190-
defaultValue: true,
190+
default: { value: true },
191191
},
192192
},
193193
}),

src/execution/getVariableSignature.ts

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import { GraphQLError } from '../error/GraphQLError.js';
22

3-
import type { VariableDefinitionNode } from '../language/ast.js';
3+
import type {
4+
ConstValueNode,
5+
VariableDefinitionNode,
6+
} from '../language/ast.js';
47
import { print } from '../language/printer.js';
58

69
import { isInputType } from '../type/definition.js';
7-
import type {
8-
GraphQLDefaultValueUsage,
9-
GraphQLInputType,
10-
GraphQLSchema,
11-
} from '../type/index.js';
10+
import type { GraphQLInputType, GraphQLSchema } from '../type/index.js';
1211

1312
import { typeFromAST } from '../utilities/typeFromAST.js';
1413

@@ -21,7 +20,8 @@ import { typeFromAST } from '../utilities/typeFromAST.js';
2120
export interface GraphQLVariableSignature {
2221
name: string;
2322
type: GraphQLInputType;
24-
defaultValue: GraphQLDefaultValueUsage | undefined;
23+
defaultValue?: never;
24+
default: { literal: ConstValueNode } | undefined;
2525
}
2626

2727
export function getVariableSignature(
@@ -46,6 +46,6 @@ export function getVariableSignature(
4646
return {
4747
name: varName,
4848
type: varType,
49-
defaultValue: defaultValue ? { literal: defaultValue } : undefined,
49+
default: defaultValue && { literal: defaultValue },
5050
};
5151
}

src/execution/values.ts

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -231,11 +231,9 @@ export function experimentalGetArgumentValues(
231231
{ nodes: node },
232232
);
233233
}
234-
if (argDef.defaultValue) {
235-
coercedValues[name] = coerceDefaultValue(
236-
argDef.defaultValue,
237-
argDef.type,
238-
);
234+
const coercedDefaultValue = coerceDefaultValue(argDef);
235+
if (coercedDefaultValue !== undefined) {
236+
coercedValues[name] = coercedDefaultValue;
239237
}
240238
continue;
241239
}
@@ -254,11 +252,9 @@ export function experimentalGetArgumentValues(
254252
!Object.hasOwn(scopedVariableValues.coerced, variableName)) &&
255253
!isRequiredArgument(argDef)
256254
) {
257-
if (argDef.defaultValue) {
258-
coercedValues[name] = coerceDefaultValue(
259-
argDef.defaultValue,
260-
argDef.type,
261-
);
255+
const coercedDefaultValue = coerceDefaultValue(argDef);
256+
if (coercedDefaultValue !== undefined) {
257+
coercedValues[name] = coercedDefaultValue;
262258
}
263259
continue;
264260
}

src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ export type {
212212
GraphQLScalarOutputValueCoercer,
213213
GraphQLScalarInputValueCoercer,
214214
GraphQLScalarInputLiteralCoercer,
215-
GraphQLDefaultValueUsage,
215+
GraphQLDefaultInput,
216216
} from './type/index.js';
217217

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

src/type/__tests__/definition-test.ts

Lines changed: 14 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -204,8 +204,8 @@ describe('Type System: Objects', () => {
204204
input: {
205205
description: 'Argument description.',
206206
type: ScalarType,
207-
defaultValue: 'DefaultValue',
208-
defaultValueLiteral: undefined,
207+
defaultValue: undefined,
208+
default: { value: 'DefaultValue' },
209209
deprecationReason: 'Argument deprecation reason.',
210210
extensions: { someExtension: 'extension' },
211211
astNode: dummyAny,
@@ -377,6 +377,7 @@ describe('Type System: Objects', () => {
377377
description: undefined,
378378
type: ScalarType,
379379
defaultValue: undefined,
380+
default: undefined,
380381
deprecationReason: undefined,
381382
extensions: {},
382383
astNode: undefined,
@@ -493,7 +494,7 @@ describe('Type System: Interfaces', () => {
493494
description: 'Argument description.',
494495
type: ScalarType,
495496
defaultValue: undefined,
496-
defaultValueLiteral: dummyAny,
497+
default: { literal: dummyAny },
497498
deprecationReason: 'Argument deprecation reason.',
498499
extensions: { someExtension: 'extension' },
499500
astNode: dummyAny,
@@ -830,8 +831,8 @@ describe('Type System: Input Objects', () => {
830831
input: {
831832
description: 'Argument description.',
832833
type: ScalarType,
833-
defaultValue: 'DefaultValue',
834-
defaultValueLiteral: undefined,
834+
defaultValue: undefined,
835+
default: { value: 'DefaultValue' },
835836
deprecationReason: 'Argument deprecation reason.',
836837
extensions: { someExtension: 'extension' },
837838
astNode: dummyAny,
@@ -860,6 +861,7 @@ describe('Type System: Input Objects', () => {
860861
description: undefined,
861862
type: ScalarType,
862863
defaultValue: undefined,
864+
default: undefined,
863865
deprecationReason: undefined,
864866
extensions: {},
865867
astNode: undefined,
@@ -879,6 +881,7 @@ describe('Type System: Input Objects', () => {
879881
description: undefined,
880882
type: ScalarType,
881883
defaultValue: undefined,
884+
default: undefined,
882885
extensions: {},
883886
deprecationReason: undefined,
884887
astNode: undefined,
@@ -927,14 +930,15 @@ describe('Type System: Input Objects', () => {
927930
const inputObjType = new GraphQLInputObjectType({
928931
name: 'SomeInputObject',
929932
fields: {
930-
f: { type: ScalarType, defaultValue: 3 },
933+
f: { type: ScalarType, default: { value: 3 } },
931934
},
932935
});
933936
expect(inputObjType.getFields().f).to.deep.include({
934937
name: 'f',
935938
description: undefined,
936939
type: ScalarType,
937-
defaultValue: { value: 3 },
940+
defaultValue: undefined,
941+
default: { value: 3 },
938942
deprecationReason: undefined,
939943
extensions: {},
940944
astNode: undefined,
@@ -947,36 +951,21 @@ describe('Type System: Input Objects', () => {
947951
fields: {
948952
f: {
949953
type: ScalarType,
950-
defaultValueLiteral: { kind: Kind.INT, value: '3' },
954+
default: { literal: { kind: Kind.INT, value: '3' } },
951955
},
952956
},
953957
});
954958
expect(inputObjType.getFields().f).to.deep.include({
955959
name: 'f',
956960
description: undefined,
957961
type: ScalarType,
958-
defaultValue: { literal: { kind: 'IntValue', value: '3' } },
962+
defaultValue: undefined,
963+
default: { literal: { kind: 'IntValue', value: '3' } },
959964
deprecationReason: undefined,
960965
extensions: {},
961966
astNode: undefined,
962967
});
963968
});
964-
965-
it('rejects an Input Object type with potentially conflicting default values', () => {
966-
const inputObjType = new GraphQLInputObjectType({
967-
name: 'SomeInputObject',
968-
fields: {
969-
f: {
970-
type: ScalarType,
971-
defaultValue: 3,
972-
defaultValueLiteral: { kind: Kind.INT, value: '3' },
973-
},
974-
},
975-
});
976-
expect(() => inputObjType.getFields()).to.throw(
977-
'Argument "f" has both a defaultValue and a defaultValueLiteral property, but only one must be provided.',
978-
);
979-
});
980969
});
981970
});
982971

src/type/__tests__/directive-test.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ describe('Type System: Directive', () => {
4545
description: undefined,
4646
type: GraphQLString,
4747
defaultValue: undefined,
48+
default: undefined,
4849
deprecationReason: undefined,
4950
extensions: {},
5051
astNode: undefined,
@@ -56,6 +57,7 @@ describe('Type System: Directive', () => {
5657
description: undefined,
5758
type: GraphQLInt,
5859
defaultValue: undefined,
60+
default: undefined,
5961
deprecationReason: undefined,
6062
extensions: {},
6163
astNode: undefined,

src/type/__tests__/enumType-test.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ const QueryType = new GraphQLObjectType({
7171
args: {
7272
fromEnum: {
7373
type: ComplexEnum,
74-
defaultValue: 'ONE',
74+
default: { value: 'ONE' },
7575
},
7676
provideGoodValue: { type: GraphQLBoolean },
7777
provideBadValue: { type: GraphQLBoolean },
@@ -90,6 +90,18 @@ const QueryType = new GraphQLObjectType({
9090
return fromEnum;
9191
},
9292
},
93+
complexEnumWithLegacyDefault: {
94+
type: ComplexEnum,
95+
args: {
96+
fromEnum: {
97+
type: ComplexEnum,
98+
defaultValue: Complex1,
99+
},
100+
},
101+
resolve(_source, { fromEnum }) {
102+
return fromEnum;
103+
},
104+
},
93105
thunkValuesString: {
94106
type: GraphQLString,
95107
args: {
@@ -442,6 +454,20 @@ describe('Type System: Enum Values', () => {
442454
});
443455
});
444456

457+
it('may be internally represented with complex values using legacy internal defaults', () => {
458+
const result = executeQuery(`
459+
{
460+
complexEnumWithLegacyDefault
461+
}
462+
`);
463+
464+
expectJSON(result).toDeepEqual({
465+
data: {
466+
complexEnumWithLegacyDefault: 'ONE',
467+
},
468+
});
469+
});
470+
445471
it('may have values specified via a callback', () => {
446472
const result = executeQuery('{ thunkValuesString(fromEnum: B) }');
447473

src/type/__tests__/predicate-test.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { DirectiveLocation } from '../../language/directiveLocation.js';
55

66
import type {
77
GraphQLArgument,
8+
GraphQLDefaultInput,
89
GraphQLInputField,
910
GraphQLInputType,
1011
} from '../definition.js';
@@ -634,7 +635,7 @@ describe('Type predicates', () => {
634635
describe('isRequiredArgument', () => {
635636
function buildArg(config: {
636637
type: GraphQLInputType;
637-
defaultValue?: unknown;
638+
default?: GraphQLDefaultInput;
638639
}): GraphQLArgument {
639640
const objectType = new GraphQLObjectType({
640641
name: 'SomeType',
@@ -660,7 +661,7 @@ describe('Type predicates', () => {
660661

661662
const optArg2 = buildArg({
662663
type: GraphQLString,
663-
defaultValue: null,
664+
default: { value: null },
664665
});
665666
expect(isRequiredArgument(optArg2)).to.equal(false);
666667

@@ -671,7 +672,7 @@ describe('Type predicates', () => {
671672

672673
const optArg4 = buildArg({
673674
type: new GraphQLNonNull(GraphQLString),
674-
defaultValue: 'default',
675+
default: { value: 'default' },
675676
});
676677
expect(isRequiredArgument(optArg4)).to.equal(false);
677678
});
@@ -680,7 +681,7 @@ describe('Type predicates', () => {
680681
describe('isRequiredInputField', () => {
681682
function buildInputField(config: {
682683
type: GraphQLInputType;
683-
defaultValue?: unknown;
684+
default?: GraphQLDefaultInput;
684685
}): GraphQLInputField {
685686
const inputObjectType = new GraphQLInputObjectType({
686687
name: 'SomeType',
@@ -706,7 +707,7 @@ describe('Type predicates', () => {
706707

707708
const optField2 = buildInputField({
708709
type: GraphQLString,
709-
defaultValue: null,
710+
default: { value: null },
710711
});
711712
expect(isRequiredInputField(optField2)).to.equal(false);
712713

@@ -717,7 +718,7 @@ describe('Type predicates', () => {
717718

718719
const optField4 = buildInputField({
719720
type: new GraphQLNonNull(GraphQLString),
720-
defaultValue: 'default',
721+
default: { value: 'default' },
721722
});
722723
expect(isRequiredInputField(optField4)).to.equal(false);
723724
});

0 commit comments

Comments
 (0)