Skip to content

Commit d05650d

Browse files
committed
Allow skipping suggestions
1 parent a1d22a2 commit d05650d

File tree

12 files changed

+183
-50
lines changed

12 files changed

+183
-50
lines changed

src/execution/execute.ts

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ export interface ValidatedExecutionArgs {
148148
typeResolver: GraphQLTypeResolver<any, any>;
149149
subscribeFieldResolver: GraphQLFieldResolver<any, any>;
150150
enableEarlyExecution: boolean;
151+
shouldProvideSuggestions: boolean;
151152
}
152153

153154
export interface ExecutionContext {
@@ -172,6 +173,7 @@ export interface ExecutionArgs {
172173
typeResolver?: Maybe<GraphQLTypeResolver<any, any>>;
173174
subscribeFieldResolver?: Maybe<GraphQLFieldResolver<any, any>>;
174175
enableEarlyExecution?: Maybe<boolean>;
176+
shouldProvideSuggestions?: Maybe<boolean>;
175177
}
176178

177179
export interface StreamUsage {
@@ -472,6 +474,7 @@ export function validateExecutionArgs(
472474
typeResolver,
473475
subscribeFieldResolver,
474476
enableEarlyExecution,
477+
shouldProvideSuggestions,
475478
} = args;
476479

477480
// If the schema used for execution is invalid, throw an error.
@@ -527,7 +530,10 @@ export function validateExecutionArgs(
527530
schema,
528531
variableDefinitions,
529532
rawVariableValues ?? {},
530-
{ maxErrors: 50 },
533+
{
534+
maxErrors: 50,
535+
shouldProvideSuggestions: shouldProvideSuggestions ?? true,
536+
},
531537
);
532538

533539
if (variableValuesOrErrors.errors) {
@@ -545,6 +551,7 @@ export function validateExecutionArgs(
545551
typeResolver: typeResolver ?? defaultTypeResolver,
546552
subscribeFieldResolver: subscribeFieldResolver ?? defaultFieldResolver,
547553
enableEarlyExecution: enableEarlyExecution === true,
554+
shouldProvideSuggestions: shouldProvideSuggestions ?? true,
548555
};
549556
}
550557

@@ -728,7 +735,8 @@ function executeField(
728735
deferMap: ReadonlyMap<DeferUsage, DeferredFragmentRecord> | undefined,
729736
): PromiseOrValue<GraphQLWrappedResult<unknown>> | undefined {
730737
const validatedExecutionArgs = exeContext.validatedExecutionArgs;
731-
const { schema, contextValue, variableValues } = validatedExecutionArgs;
738+
const { schema, contextValue, variableValues, shouldProvideSuggestions } =
739+
validatedExecutionArgs;
732740
const fieldName = fieldGroup[0].node.name.value;
733741
const fieldDef = schema.getField(parentType, fieldName);
734742
if (!fieldDef) {
@@ -755,6 +763,7 @@ function executeField(
755763
fieldGroup[0].node,
756764
fieldDef.args,
757765
variableValues,
766+
shouldProvideSuggestions,
758767
fieldGroup[0].fragmentVariableValues,
759768
);
760769

@@ -1058,12 +1067,14 @@ function getStreamUsage(
10581067
._streamUsage;
10591068
}
10601069

1061-
const { operation, variableValues } = validatedExecutionArgs;
1070+
const { operation, variableValues, shouldProvideSuggestions } =
1071+
validatedExecutionArgs;
10621072
// validation only allows equivalent streams on multiple fields, so it is
10631073
// safe to only check the first fieldNode for the stream directive
10641074
const stream = getDirectiveValues(
10651075
GraphQLStreamDirective,
10661076
fieldGroup[0].node,
1077+
shouldProvideSuggestions,
10671078
variableValues,
10681079
fieldGroup[0].fragmentVariableValues,
10691080
);
@@ -2072,7 +2083,12 @@ function executeSubscription(
20722083

20732084
// Build a JS object of arguments from the field.arguments AST, using the
20742085
// variables scope to fulfill any variable references.
2075-
const args = getArgumentValues(fieldDef, fieldNodes[0], variableValues);
2086+
const args = getArgumentValues(
2087+
fieldDef,
2088+
fieldNodes[0],
2089+
validatedExecutionArgs.shouldProvideSuggestions,
2090+
variableValues,
2091+
);
20762092

20772093
// Call the `subscribe()` resolver or the default resolver to produce an
20782094
// AsyncIterable yielding raw payloads.

src/execution/values.ts

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,11 @@ export function getVariableValues(
5555
schema: GraphQLSchema,
5656
varDefNodes: ReadonlyArray<VariableDefinitionNode>,
5757
inputs: { readonly [variable: string]: unknown },
58-
options?: { maxErrors?: number },
58+
options?: { maxErrors?: number; shouldProvideSuggestions?: boolean },
5959
): VariableValuesOrErrors {
6060
const errors: Array<GraphQLError> = [];
6161
const maxErrors = options?.maxErrors;
62+
const shouldProvideSuggestions = options?.shouldProvideSuggestions ?? true;
6263
try {
6364
const variableValues = coerceVariableValues(
6465
schema,
@@ -72,6 +73,7 @@ export function getVariableValues(
7273
}
7374
errors.push(error);
7475
},
76+
shouldProvideSuggestions,
7577
);
7678

7779
if (errors.length === 0) {
@@ -89,6 +91,7 @@ function coerceVariableValues(
8991
varDefNodes: ReadonlyArray<VariableDefinitionNode>,
9092
inputs: { readonly [variable: string]: unknown },
9193
onError: (error: GraphQLError) => void,
94+
shouldProvideSuggestions: boolean,
9295
): VariableValues {
9396
const sources: ObjMap<VariableValueSource> = Object.create(null);
9497
const coerced: ObjMap<unknown> = Object.create(null);
@@ -105,7 +108,11 @@ function coerceVariableValues(
105108
const defaultValue = varSignature.defaultValue;
106109
if (defaultValue) {
107110
sources[varName] = { signature: varSignature };
108-
coerced[varName] = coerceDefaultValue(defaultValue, varType);
111+
coerced[varName] = coerceDefaultValue(
112+
defaultValue,
113+
varType,
114+
shouldProvideSuggestions,
115+
);
109116
} else if (isNonNullType(varType)) {
110117
const varTypeStr = inspect(varType);
111118
onError(
@@ -136,6 +143,7 @@ function coerceVariableValues(
136143
coerced[varName] = coerceInputValue(
137144
value,
138145
varType,
146+
shouldProvideSuggestions,
139147
(path, invalidValue, error) => {
140148
let prefix =
141149
`Variable "$${varName}" got invalid value ` + inspect(invalidValue);
@@ -159,6 +167,7 @@ export function getFragmentVariableValues(
159167
fragmentSpreadNode: FragmentSpreadNode,
160168
fragmentSignatures: ReadOnlyObjMap<GraphQLVariableSignature>,
161169
variableValues: VariableValues,
170+
shouldProvideSuggestions: boolean,
162171
fragmentVariableValues?: Maybe<VariableValues>,
163172
): VariableValues {
164173
const varSignatures: Array<GraphQLVariableSignature> = [];
@@ -177,6 +186,7 @@ export function getFragmentVariableValues(
177186
fragmentSpreadNode,
178187
varSignatures,
179188
variableValues,
189+
shouldProvideSuggestions,
180190
fragmentVariableValues,
181191
);
182192

@@ -194,15 +204,22 @@ export function getFragmentVariableValues(
194204
export function getArgumentValues(
195205
def: GraphQLField<unknown, unknown> | GraphQLDirective,
196206
node: FieldNode | DirectiveNode,
207+
shouldProvideSuggestions: boolean,
197208
variableValues?: Maybe<VariableValues>,
198209
): { [argument: string]: unknown } {
199-
return experimentalGetArgumentValues(node, def.args, variableValues);
210+
return experimentalGetArgumentValues(
211+
node,
212+
def.args,
213+
variableValues,
214+
shouldProvideSuggestions,
215+
);
200216
}
201217

202218
export function experimentalGetArgumentValues(
203219
node: FieldNode | DirectiveNode | FragmentSpreadNode,
204220
argDefs: ReadonlyArray<GraphQLArgument | GraphQLVariableSignature>,
205221
variableValues: Maybe<VariableValues>,
222+
shouldProvideSuggestions: boolean,
206223
fragmentVariablesValues?: Maybe<VariableValues>,
207224
): { [argument: string]: unknown } {
208225
const coercedValues: { [argument: string]: unknown } = {};
@@ -222,6 +239,7 @@ export function experimentalGetArgumentValues(
222239
coercedValues[name] = coerceDefaultValue(
223240
argDef.defaultValue,
224241
argDef.type,
242+
shouldProvideSuggestions,
225243
);
226244
} else if (isNonNullType(argType)) {
227245
throw new GraphQLError(
@@ -251,6 +269,7 @@ export function experimentalGetArgumentValues(
251269
coercedValues[name] = coerceDefaultValue(
252270
argDef.defaultValue,
253271
argDef.type,
272+
shouldProvideSuggestions,
254273
);
255274
} else if (isNonNullType(argType)) {
256275
throw new GraphQLError(
@@ -275,6 +294,7 @@ export function experimentalGetArgumentValues(
275294
const coercedValue = coerceInputLiteral(
276295
valueNode,
277296
argType,
297+
shouldProvideSuggestions,
278298
variableValues,
279299
fragmentVariablesValues,
280300
);
@@ -308,6 +328,7 @@ export function experimentalGetArgumentValues(
308328
export function getDirectiveValues(
309329
directiveDef: GraphQLDirective,
310330
node: { readonly directives?: ReadonlyArray<DirectiveNode> | undefined },
331+
shouldProvideSuggestions: boolean,
311332
variableValues?: Maybe<VariableValues>,
312333
fragmentVariableValues?: Maybe<VariableValues>,
313334
): undefined | { [argument: string]: unknown } {
@@ -320,6 +341,7 @@ export function getDirectiveValues(
320341
directiveNode,
321342
directiveDef.args,
322343
variableValues,
344+
shouldProvideSuggestions,
323345
fragmentVariableValues,
324346
);
325347
}

src/type/definition.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1422,20 +1422,27 @@ export class GraphQLEnumType /* <T> */ {
14221422
return enumValue.name;
14231423
}
14241424

1425-
parseValue(inputValue: unknown): Maybe<any> /* T */ {
1425+
parseValue(
1426+
inputValue: unknown,
1427+
shouldProvideSuggestions: boolean,
1428+
): Maybe<any> /* T */ {
14261429
if (typeof inputValue !== 'string') {
14271430
const valueStr = inspect(inputValue);
14281431
throw new GraphQLError(
14291432
`Enum "${this.name}" cannot represent non-string value: ${valueStr}.` +
1430-
didYouMeanEnumValue(this, valueStr),
1433+
(shouldProvideSuggestions
1434+
? didYouMeanEnumValue(this, valueStr)
1435+
: ''),
14311436
);
14321437
}
14331438

14341439
const enumValue = this.getValue(inputValue);
14351440
if (enumValue == null) {
14361441
throw new GraphQLError(
14371442
`Value "${inputValue}" does not exist in "${this.name}" enum.` +
1438-
didYouMeanEnumValue(this, inputValue),
1443+
(shouldProvideSuggestions
1444+
? didYouMeanEnumValue(this, inputValue)
1445+
: ''),
14391446
);
14401447
}
14411448
return enumValue.value;
@@ -1445,17 +1452,24 @@ export class GraphQLEnumType /* <T> */ {
14451452
parseLiteral(
14461453
valueNode: ValueNode,
14471454
_variables: Maybe<ObjMap<unknown>>,
1455+
shouldProvideSuggestions: boolean,
14481456
): Maybe<any> /* T */ {
14491457
// Note: variables will be resolved to a value before calling this function.
1450-
return this.parseConstLiteral(valueNode as ConstValueNode);
1458+
return this.parseConstLiteral(
1459+
valueNode as ConstValueNode,
1460+
shouldProvideSuggestions,
1461+
);
14511462
}
14521463

1453-
parseConstLiteral(valueNode: ConstValueNode): Maybe<any> /* T */ {
1464+
parseConstLiteral(
1465+
valueNode: ConstValueNode,
1466+
shouldProvideSuggestions: boolean,
1467+
): Maybe<any> /* T */ {
14541468
if (valueNode.kind !== Kind.ENUM) {
14551469
const valueStr = print(valueNode);
14561470
throw new GraphQLError(
14571471
`Enum "${this.name}" cannot represent non-enum value: ${valueStr}.` +
1458-
didYouMeanEnumValue(this, valueStr),
1472+
(shouldProvideSuggestions ? didYouMeanEnumValue(this, valueStr) : ''),
14591473
{ nodes: valueNode },
14601474
);
14611475
}
@@ -1465,7 +1479,9 @@ export class GraphQLEnumType /* <T> */ {
14651479
const valueStr = print(valueNode);
14661480
throw new GraphQLError(
14671481
`Value "${valueStr}" does not exist in "${this.name}" enum.` +
1468-
didYouMeanEnumValue(this, valueStr),
1482+
(shouldProvideSuggestions
1483+
? didYouMeanEnumValue(this, valueStr)
1484+
: ''),
14691485
{ nodes: valueNode },
14701486
);
14711487
}

src/utilities/__tests__/coerceInputValue-test.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ function coerceValue(
5555
const value = coerceInputValue(
5656
inputValue,
5757
type,
58+
true,
5859
(path, invalidValue, error) => {
5960
errors.push({ path, value: invalidValue, error: error.message });
6061
},
@@ -538,7 +539,7 @@ describe('coerceInputValue', () => {
538539
describe('with default onError', () => {
539540
it('throw error without path', () => {
540541
expect(() =>
541-
coerceInputValue(null, new GraphQLNonNull(GraphQLInt)),
542+
coerceInputValue(null, new GraphQLNonNull(GraphQLInt), true),
542543
).to.throw(
543544
'Invalid value null: Expected non-nullable type "Int!" not to be null.',
544545
);
@@ -549,6 +550,7 @@ describe('coerceInputValue', () => {
549550
coerceInputValue(
550551
[null],
551552
new GraphQLList(new GraphQLNonNull(GraphQLInt)),
553+
true,
552554
),
553555
).to.throw(
554556
'Invalid value null at "value[0]": Expected non-nullable type "Int!" not to be null.',
@@ -565,7 +567,7 @@ describe('coerceInputLiteral', () => {
565567
variableValues?: VariableValues,
566568
) {
567569
const ast = parseValue(valueText);
568-
const value = coerceInputLiteral(ast, type, variableValues);
570+
const value = coerceInputLiteral(ast, type, true, variableValues);
569571
expect(value).to.deep.equal(expected);
570572
}
571573

@@ -892,10 +894,14 @@ describe('coerceDefaultValue', () => {
892894
const defaultValueUsage = {
893895
literal: { kind: Kind.STRING, value: 'hello' },
894896
} as const;
895-
expect(coerceDefaultValue(defaultValueUsage, spyScalar)).to.equal('hello');
897+
expect(coerceDefaultValue(defaultValueUsage, spyScalar, true)).to.equal(
898+
'hello',
899+
);
896900

897901
// Call a second time
898-
expect(coerceDefaultValue(defaultValueUsage, spyScalar)).to.equal('hello');
902+
expect(coerceDefaultValue(defaultValueUsage, spyScalar, true)).to.equal(
903+
'hello',
904+
);
899905
expect(parseValueCalls).to.deep.equal(['hello']);
900906
});
901907
});

0 commit comments

Comments
 (0)