Skip to content

Commit 8cb3c94

Browse files
committed
Recognize in introspection, enable disabling null bubbling
1 parent cecaa88 commit 8cb3c94

File tree

3 files changed

+98
-1
lines changed

3 files changed

+98
-1
lines changed

src/execution/execute.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,11 @@ export interface ExecutionArgs {
154154
fieldResolver?: Maybe<GraphQLFieldResolver<any, any>>;
155155
typeResolver?: Maybe<GraphQLTypeResolver<any, any>>;
156156
subscribeFieldResolver?: Maybe<GraphQLFieldResolver<any, any>>;
157+
/**
158+
* @default {true}
159+
* @experimental
160+
*/
161+
errorPropagation?: boolean;
157162
}
158163

159164
/**
@@ -288,6 +293,7 @@ export function buildExecutionContext(
288293
fieldResolver,
289294
typeResolver,
290295
subscribeFieldResolver,
296+
errorPropagation,
291297
} = args;
292298

293299
let operation: OperationDefinitionNode | undefined;
@@ -349,6 +355,7 @@ export function buildExecutionContext(
349355
typeResolver: typeResolver ?? defaultTypeResolver,
350356
subscribeFieldResolver: subscribeFieldResolver ?? defaultFieldResolver,
351357
errors: [],
358+
errorPropagation: errorPropagation ?? true,
352359
};
353360
}
354361

@@ -587,6 +594,7 @@ export function buildResolveInfo(
587594
rootValue: exeContext.rootValue,
588595
operation: exeContext.operation,
589596
variableValues: exeContext.variableValues,
597+
errorPropagation: exeContext.errorPropagation,
590598
};
591599
}
592600

src/type/definition.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1087,6 +1087,8 @@ export interface GraphQLResolveInfo {
10871087
readonly rootValue: unknown;
10881088
readonly operation: OperationDefinitionNode;
10891089
readonly variableValues: { [variable: string]: unknown };
1090+
/** @experimental */
1091+
readonly errorPropagation: boolean;
10901092
}
10911093

10921094
/**

src/type/introspection.ts

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
GraphQLEnumType,
2020
GraphQLList,
2121
GraphQLNonNull,
22+
GraphQLSemanticNonNull,
2223
GraphQLObjectType,
2324
isAbstractType,
2425
isEnumType,
@@ -205,6 +206,40 @@ export const __DirectiveLocation: GraphQLEnumType = new GraphQLEnumType({
205206
},
206207
});
207208

209+
// TODO: rename enum and options
210+
enum TypeNullability {
211+
AUTO = 'AUTO',
212+
TRADITIONAL = 'TRADITIONAL',
213+
SEMANTIC = 'SEMANTIC',
214+
FULL = 'FULL',
215+
}
216+
217+
// TODO: rename
218+
export const __TypeNullability: GraphQLEnumType = new GraphQLEnumType({
219+
name: '__TypeNullability',
220+
description: 'TODO',
221+
values: {
222+
AUTO: {
223+
value: TypeNullability.AUTO,
224+
description:
225+
'Determines nullability mode based on errorPropagation mode.',
226+
},
227+
TRADITIONAL: {
228+
value: TypeNullability.TRADITIONAL,
229+
description: 'Turn semantic-non-null types into nullable types.',
230+
},
231+
SEMANTIC: {
232+
value: TypeNullability.SEMANTIC,
233+
description: 'Turn non-null types into semantic-non-null types.',
234+
},
235+
FULL: {
236+
value: TypeNullability.FULL,
237+
description:
238+
'Render the true nullability in the schema; be prepared for new types of nullability in future!',
239+
},
240+
},
241+
});
242+
208243
export const __Type: GraphQLObjectType = new GraphQLObjectType({
209244
name: '__Type',
210245
description:
@@ -370,7 +405,25 @@ export const __Field: GraphQLObjectType = new GraphQLObjectType({
370405
},
371406
type: {
372407
type: new GraphQLNonNull(__Type),
373-
resolve: (field) => field.type,
408+
args: {
409+
nullability: {
410+
type: __TypeNullability,
411+
defaultValue: 'AUTO',
412+
},
413+
},
414+
resolve: (field, { nullability }, _context, info) => {
415+
if (nullability === TypeNullability.FULL) {
416+
return field.type;
417+
} else {
418+
const mode =
419+
nullability === TypeNullability.AUTO
420+
? info.errorPropagation
421+
? TypeNullability.TRADITIONAL
422+
: TypeNullability.SEMANTIC
423+
: nullability;
424+
return convertOutputTypeToNullabilityMode(field.type, mode);
425+
}
426+
},
374427
},
375428
isDeprecated: {
376429
type: new GraphQLNonNull(GraphQLBoolean),
@@ -383,6 +436,40 @@ export const __Field: GraphQLObjectType = new GraphQLObjectType({
383436
} as GraphQLFieldConfigMap<GraphQLField<unknown, unknown>, unknown>),
384437
});
385438

439+
// TODO: move this elsewhere, rename, memoize
440+
function convertOutputTypeToNullabilityMode(
441+
type: GraphQLType,
442+
mode: TypeNullability.TRADITIONAL | TypeNullability.SEMANTIC,
443+
): GraphQLType {
444+
if (mode === TypeNullability.TRADITIONAL) {
445+
if (isNonNullType(type)) {
446+
return new GraphQLNonNull(
447+
convertOutputTypeToNullabilityMode(type.ofType, mode),
448+
);
449+
} else if (isSemanticNonNullType(type)) {
450+
return convertOutputTypeToNullabilityMode(type.ofType, mode);
451+
} else if (isListType(type)) {
452+
return new GraphQLList(
453+
convertOutputTypeToNullabilityMode(type.ofType, mode),
454+
);
455+
} else {
456+
return type;
457+
}
458+
} else {
459+
if (isNonNullType(type) || isSemanticNonNullType(type)) {
460+
return new GraphQLSemanticNonNull(
461+
convertOutputTypeToNullabilityMode(type.ofType, mode),
462+
);
463+
} else if (isListType(type)) {
464+
return new GraphQLList(
465+
convertOutputTypeToNullabilityMode(type.ofType, mode),
466+
);
467+
} else {
468+
return type;
469+
}
470+
}
471+
}
472+
386473
export const __InputValue: GraphQLObjectType = new GraphQLObjectType({
387474
name: '__InputValue',
388475
description:

0 commit comments

Comments
 (0)