Skip to content

Commit 70e4f4d

Browse files
committed
Allow skipping suggestions
1 parent a1d22a2 commit 70e4f4d

12 files changed

+111
-51
lines changed

src/execution/execute.ts

Lines changed: 10 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,7 @@ export function validateExecutionArgs(
527530
schema,
528531
variableDefinitions,
529532
rawVariableValues ?? {},
530-
{ maxErrors: 50 },
533+
{ maxErrors: 50, shouldProvideSuggestions: shouldProvideSuggestions??true },
531534
);
532535

533536
if (variableValuesOrErrors.errors) {
@@ -545,6 +548,7 @@ export function validateExecutionArgs(
545548
typeResolver: typeResolver ?? defaultTypeResolver,
546549
subscribeFieldResolver: subscribeFieldResolver ?? defaultFieldResolver,
547550
enableEarlyExecution: enableEarlyExecution === true,
551+
shouldProvideSuggestions: shouldProvideSuggestions??true
548552
};
549553
}
550554

@@ -728,7 +732,7 @@ function executeField(
728732
deferMap: ReadonlyMap<DeferUsage, DeferredFragmentRecord> | undefined,
729733
): PromiseOrValue<GraphQLWrappedResult<unknown>> | undefined {
730734
const validatedExecutionArgs = exeContext.validatedExecutionArgs;
731-
const { schema, contextValue, variableValues } = validatedExecutionArgs;
735+
const { schema, contextValue, variableValues,shouldProvideSuggestions } = validatedExecutionArgs;
732736
const fieldName = fieldGroup[0].node.name.value;
733737
const fieldDef = schema.getField(parentType, fieldName);
734738
if (!fieldDef) {
@@ -755,6 +759,7 @@ function executeField(
755759
fieldGroup[0].node,
756760
fieldDef.args,
757761
variableValues,
762+
shouldProvideSuggestions,
758763
fieldGroup[0].fragmentVariableValues,
759764
);
760765

@@ -1058,12 +1063,13 @@ function getStreamUsage(
10581063
._streamUsage;
10591064
}
10601065

1061-
const { operation, variableValues } = validatedExecutionArgs;
1066+
const { operation, variableValues, shouldProvideSuggestions } = validatedExecutionArgs;
10621067
// validation only allows equivalent streams on multiple fields, so it is
10631068
// safe to only check the first fieldNode for the stream directive
10641069
const stream = getDirectiveValues(
10651070
GraphQLStreamDirective,
10661071
fieldGroup[0].node,
1072+
shouldProvideSuggestions,
10671073
variableValues,
10681074
fieldGroup[0].fragmentVariableValues,
10691075
);
@@ -2072,7 +2078,7 @@ function executeSubscription(
20722078

20732079
// Build a JS object of arguments from the field.arguments AST, using the
20742080
// variables scope to fulfill any variable references.
2075-
const args = getArgumentValues(fieldDef, fieldNodes[0], variableValues);
2081+
const args = getArgumentValues(fieldDef, fieldNodes[0], validatedExecutionArgs.shouldProvideSuggestions, variableValues);
20762082

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

src/execution/values.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ 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;
@@ -72,6 +72,7 @@ export function getVariableValues(
7272
}
7373
errors.push(error);
7474
},
75+
options?.shouldProvideSuggestions?? true
7576
);
7677

7778
if (errors.length === 0) {
@@ -89,6 +90,7 @@ function coerceVariableValues(
8990
varDefNodes: ReadonlyArray<VariableDefinitionNode>,
9091
inputs: { readonly [variable: string]: unknown },
9192
onError: (error: GraphQLError) => void,
93+
shouldProvideSuggestions: boolean
9294
): VariableValues {
9395
const sources: ObjMap<VariableValueSource> = Object.create(null);
9496
const coerced: ObjMap<unknown> = Object.create(null);
@@ -105,7 +107,7 @@ function coerceVariableValues(
105107
const defaultValue = varSignature.defaultValue;
106108
if (defaultValue) {
107109
sources[varName] = { signature: varSignature };
108-
coerced[varName] = coerceDefaultValue(defaultValue, varType);
110+
coerced[varName] = coerceDefaultValue(defaultValue, varType, shouldProvideSuggestions);
109111
} else if (isNonNullType(varType)) {
110112
const varTypeStr = inspect(varType);
111113
onError(
@@ -149,6 +151,7 @@ function coerceVariableValues(
149151
}),
150152
);
151153
},
154+
shouldProvideSuggestions
152155
);
153156
}
154157

@@ -159,6 +162,7 @@ export function getFragmentVariableValues(
159162
fragmentSpreadNode: FragmentSpreadNode,
160163
fragmentSignatures: ReadOnlyObjMap<GraphQLVariableSignature>,
161164
variableValues: VariableValues,
165+
shouldProvideSuggestions: boolean,
162166
fragmentVariableValues?: Maybe<VariableValues>,
163167
): VariableValues {
164168
const varSignatures: Array<GraphQLVariableSignature> = [];
@@ -177,8 +181,9 @@ export function getFragmentVariableValues(
177181
fragmentSpreadNode,
178182
varSignatures,
179183
variableValues,
184+
shouldProvideSuggestions,
180185
fragmentVariableValues,
181-
);
186+
)
182187

183188
return { sources, coerced };
184189
}
@@ -194,15 +199,17 @@ export function getFragmentVariableValues(
194199
export function getArgumentValues(
195200
def: GraphQLField<unknown, unknown> | GraphQLDirective,
196201
node: FieldNode | DirectiveNode,
202+
shouldProvideSuggestions: boolean,
197203
variableValues?: Maybe<VariableValues>,
198-
): { [argument: string]: unknown } {
199-
return experimentalGetArgumentValues(node, def.args, variableValues);
204+
): { [argument: string]: unknown } {
205+
return experimentalGetArgumentValues(node, def.args, variableValues, shouldProvideSuggestions);
200206
}
201207

202208
export function experimentalGetArgumentValues(
203209
node: FieldNode | DirectiveNode | FragmentSpreadNode,
204210
argDefs: ReadonlyArray<GraphQLArgument | GraphQLVariableSignature>,
205211
variableValues: Maybe<VariableValues>,
212+
shouldProvideSuggestions: boolean,
206213
fragmentVariablesValues?: Maybe<VariableValues>,
207214
): { [argument: string]: unknown } {
208215
const coercedValues: { [argument: string]: unknown } = {};
@@ -222,6 +229,7 @@ export function experimentalGetArgumentValues(
222229
coercedValues[name] = coerceDefaultValue(
223230
argDef.defaultValue,
224231
argDef.type,
232+
shouldProvideSuggestions
225233
);
226234
} else if (isNonNullType(argType)) {
227235
throw new GraphQLError(
@@ -251,6 +259,7 @@ export function experimentalGetArgumentValues(
251259
coercedValues[name] = coerceDefaultValue(
252260
argDef.defaultValue,
253261
argDef.type,
262+
shouldProvideSuggestions
254263
);
255264
} else if (isNonNullType(argType)) {
256265
throw new GraphQLError(
@@ -275,6 +284,7 @@ export function experimentalGetArgumentValues(
275284
const coercedValue = coerceInputLiteral(
276285
valueNode,
277286
argType,
287+
shouldProvideSuggestions,
278288
variableValues,
279289
fragmentVariablesValues,
280290
);
@@ -308,6 +318,7 @@ export function experimentalGetArgumentValues(
308318
export function getDirectiveValues(
309319
directiveDef: GraphQLDirective,
310320
node: { readonly directives?: ReadonlyArray<DirectiveNode> | undefined },
321+
shouldProvideSuggestions: boolean,
311322
variableValues?: Maybe<VariableValues>,
312323
fragmentVariableValues?: Maybe<VariableValues>,
313324
): undefined | { [argument: string]: unknown } {
@@ -320,6 +331,7 @@ export function getDirectiveValues(
320331
directiveNode,
321332
directiveDef.args,
322333
variableValues,
334+
shouldProvideSuggestions,
323335
fragmentVariableValues,
324336
);
325337
}

src/type/definition.ts

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

1425-
parseValue(inputValue: unknown): Maybe<any> /* T */ {
1425+
parseValue(inputValue: unknown, shouldProvideSuggestions: boolean): Maybe<any> /* T */ {
14261426
if (typeof inputValue !== 'string') {
14271427
const valueStr = inspect(inputValue);
14281428
throw new GraphQLError(
14291429
`Enum "${this.name}" cannot represent non-string value: ${valueStr}.` +
1430-
didYouMeanEnumValue(this, valueStr),
1430+
shouldProvideSuggestions??true ? didYouMeanEnumValue(this, valueStr) : "",
14311431
);
14321432
}
14331433

14341434
const enumValue = this.getValue(inputValue);
14351435
if (enumValue == null) {
14361436
throw new GraphQLError(
14371437
`Value "${inputValue}" does not exist in "${this.name}" enum.` +
1438-
didYouMeanEnumValue(this, inputValue),
1438+
shouldProvideSuggestions??true ? didYouMeanEnumValue(this, inputValue) : "",
14391439
);
14401440
}
14411441
return enumValue.value;
@@ -1445,17 +1445,18 @@ export class GraphQLEnumType /* <T> */ {
14451445
parseLiteral(
14461446
valueNode: ValueNode,
14471447
_variables: Maybe<ObjMap<unknown>>,
1448+
shouldProvideSuggestions: boolean
14481449
): Maybe<any> /* T */ {
14491450
// Note: variables will be resolved to a value before calling this function.
1450-
return this.parseConstLiteral(valueNode as ConstValueNode);
1451+
return this.parseConstLiteral(valueNode as ConstValueNode, shouldProvideSuggestions);
14511452
}
14521453

1453-
parseConstLiteral(valueNode: ConstValueNode): Maybe<any> /* T */ {
1454+
parseConstLiteral(valueNode: ConstValueNode, shouldProvideSuggestions: boolean): Maybe<any> /* T */ {
14541455
if (valueNode.kind !== Kind.ENUM) {
14551456
const valueStr = print(valueNode);
14561457
throw new GraphQLError(
14571458
`Enum "${this.name}" cannot represent non-enum value: ${valueStr}.` +
1458-
didYouMeanEnumValue(this, valueStr),
1459+
shouldProvideSuggestions ?didYouMeanEnumValue(this, valueStr) : "",
14591460
{ nodes: valueNode },
14601461
);
14611462
}
@@ -1465,7 +1466,7 @@ export class GraphQLEnumType /* <T> */ {
14651466
const valueStr = print(valueNode);
14661467
throw new GraphQLError(
14671468
`Value "${valueStr}" does not exist in "${this.name}" enum.` +
1468-
didYouMeanEnumValue(this, valueStr),
1469+
shouldProvideSuggestions ?didYouMeanEnumValue(this, valueStr) : "",
14691470
{ nodes: valueNode },
14701471
);
14711472
}

src/utilities/__tests__/coerceInputValue-test.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ function coerceValue(
5858
(path, invalidValue, error) => {
5959
errors.push({ path, value: invalidValue, error: error.message });
6060
},
61+
true
6162
);
6263

6364
return { errors, value };
@@ -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), undefined, true),
542543
).to.throw(
543544
'Invalid value null: Expected non-nullable type "Int!" not to be null.',
544545
);
@@ -549,6 +550,8 @@ describe('coerceInputValue', () => {
549550
coerceInputValue(
550551
[null],
551552
new GraphQLList(new GraphQLNonNull(GraphQLInt)),
553+
undefined,
554+
true
552555
),
553556
).to.throw(
554557
'Invalid value null at "value[0]": Expected non-nullable type "Int!" not to be null.',
@@ -565,7 +568,7 @@ describe('coerceInputLiteral', () => {
565568
variableValues?: VariableValues,
566569
) {
567570
const ast = parseValue(valueText);
568-
const value = coerceInputLiteral(ast, type, variableValues);
571+
const value = coerceInputLiteral(ast, type, variableValues, true);
569572
expect(value).to.deep.equal(expected);
570573
}
571574

@@ -892,10 +895,10 @@ describe('coerceDefaultValue', () => {
892895
const defaultValueUsage = {
893896
literal: { kind: Kind.STRING, value: 'hello' },
894897
} as const;
895-
expect(coerceDefaultValue(defaultValueUsage, spyScalar)).to.equal('hello');
898+
expect(coerceDefaultValue(defaultValueUsage, spyScalar, true)).to.equal('hello');
896899

897900
// Call a second time
898-
expect(coerceDefaultValue(defaultValueUsage, spyScalar)).to.equal('hello');
901+
expect(coerceDefaultValue(defaultValueUsage, spyScalar, true)).to.equal('hello');
899902
expect(parseValueCalls).to.deep.equal(['hello']);
900903
});
901904
});

0 commit comments

Comments
 (0)