Skip to content

Commit 6731d9a

Browse files
committed
Handle isNonNullType
1 parent 6bcede2 commit 6731d9a

19 files changed

+155
-7
lines changed

src/execution/execute.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import {
4242
isLeafType,
4343
isListType,
4444
isNonNullType,
45+
isSemanticNonNullType,
4546
isObjectType,
4647
} from '../type/definition';
4748
import {
@@ -115,6 +116,7 @@ export interface ExecutionContext {
115116
typeResolver: GraphQLTypeResolver<any, any>;
116117
subscribeFieldResolver: GraphQLFieldResolver<any, any>;
117118
errors: Array<GraphQLError>;
119+
errorPropagation: boolean;
118120
}
119121

120122
/**
@@ -595,7 +597,7 @@ function handleFieldError(
595597
): null {
596598
// If the field type is non-nullable, then it is resolved without any
597599
// protection from errors, however it still properly locates the error.
598-
if (isNonNullType(returnType)) {
600+
if (exeContext.errorPropagation && isNonNullType(returnType)) {
599601
throw error;
600602
}
601603

@@ -658,6 +660,25 @@ function completeValue(
658660
return completed;
659661
}
660662

663+
// If field type is SemanticNonNull, complete for inner type, and throw field error
664+
// if result is null.
665+
if (isSemanticNonNullType(returnType)) {
666+
const completed = completeValue(
667+
exeContext,
668+
returnType.ofType,
669+
fieldNodes,
670+
info,
671+
path,
672+
result,
673+
);
674+
if (completed === null) {
675+
throw new Error(
676+
`Cannot return null for semantic-non-nullable field ${info.parentType.name}.${info.fieldName}.`,
677+
);
678+
}
679+
return completed;
680+
}
681+
661682
// If result value is null or undefined then return null.
662683
if (result == null) {
663684
return null;

src/index.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export {
4848
GraphQLInputObjectType,
4949
GraphQLList,
5050
GraphQLNonNull,
51+
GraphQLSemanticNonNull,
5152
// Standard GraphQL Scalars
5253
specifiedScalarTypes,
5354
GraphQLInt,
@@ -95,6 +96,7 @@ export {
9596
isInputObjectType,
9697
isListType,
9798
isNonNullType,
99+
isSemanticNonNullType,
98100
isInputType,
99101
isOutputType,
100102
isLeafType,
@@ -120,6 +122,7 @@ export {
120122
assertInputObjectType,
121123
assertListType,
122124
assertNonNullType,
125+
assertSemanticNonNullType,
123126
assertInputType,
124127
assertOutputType,
125128
assertLeafType,
@@ -287,6 +290,7 @@ export type {
287290
NamedTypeNode,
288291
ListTypeNode,
289292
NonNullTypeNode,
293+
SemanticNonNullTypeNode,
290294
TypeSystemDefinitionNode,
291295
SchemaDefinitionNode,
292296
OperationTypeDefinitionNode,
@@ -480,6 +484,7 @@ export type {
480484
IntrospectionNamedTypeRef,
481485
IntrospectionListTypeRef,
482486
IntrospectionNonNullTypeRef,
487+
IntrospectionSemanticNonNullTypeRef,
483488
IntrospectionField,
484489
IntrospectionInputValue,
485490
IntrospectionEnumValue,

src/language/ast.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,6 +540,12 @@ export interface NonNullTypeNode {
540540
readonly type: NamedTypeNode | ListTypeNode;
541541
}
542542

543+
export interface SemanticNonNullTypeNode {
544+
readonly kind: Kind.SEMANTIC_NON_NULL_TYPE;
545+
readonly loc?: Location;
546+
readonly type: NamedTypeNode | ListTypeNode;
547+
}
548+
543549
/** Type System Definition */
544550

545551
export type TypeSystemDefinitionNode =

src/language/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@ export type {
6767
NamedTypeNode,
6868
ListTypeNode,
6969
NonNullTypeNode,
70+
SemanticNonNullTypeNode,
7071
TypeSystemDefinitionNode,
7172
SchemaDefinitionNode,
7273
OperationTypeDefinitionNode,

src/language/kinds.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ enum Kind {
3737
NAMED_TYPE = 'NamedType',
3838
LIST_TYPE = 'ListType',
3939
NON_NULL_TYPE = 'NonNullType',
40+
SEMANTIC_NON_NULL_TYPE = 'SemanticNonNullType',
4041

4142
/** Type System Definitions */
4243
SCHEMA_DEFINITION = 'SchemaDefinition',

src/type/__tests__/predicate-test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
assertListType,
2020
assertNamedType,
2121
assertNonNullType,
22+
assertSemanticNonNullType,
2223
assertNullableType,
2324
assertObjectType,
2425
assertOutputType,
@@ -33,6 +34,7 @@ import {
3334
GraphQLInterfaceType,
3435
GraphQLList,
3536
GraphQLNonNull,
37+
GraphQLSemanticNonNull,
3638
GraphQLObjectType,
3739
GraphQLScalarType,
3840
GraphQLUnionType,
@@ -46,6 +48,7 @@ import {
4648
isListType,
4749
isNamedType,
4850
isNonNullType,
51+
isSemanticNonNullType,
4952
isNullableType,
5053
isObjectType,
5154
isOutputType,
@@ -298,6 +301,45 @@ describe('Type predicates', () => {
298301
expect(() =>
299302
assertNonNullType(new GraphQLList(new GraphQLNonNull(ObjectType))),
300303
).to.throw();
304+
expect(isNonNullType(new GraphQLSemanticNonNull(ObjectType))).to.equal(
305+
false,
306+
);
307+
expect(() =>
308+
assertNonNullType(new GraphQLSemanticNonNull(ObjectType)),
309+
).to.throw();
310+
});
311+
});
312+
313+
describe('isSemanticNonNullType', () => {
314+
it('returns true for a semantic-non-null wrapped type', () => {
315+
expect(
316+
isSemanticNonNullType(new GraphQLSemanticNonNull(ObjectType)),
317+
).to.equal(true);
318+
expect(() =>
319+
assertSemanticNonNullType(new GraphQLSemanticNonNull(ObjectType)),
320+
).to.not.throw();
321+
});
322+
323+
it('returns false for an unwrapped type', () => {
324+
expect(isSemanticNonNullType(ObjectType)).to.equal(false);
325+
expect(() => assertSemanticNonNullType(ObjectType)).to.throw();
326+
});
327+
328+
it('returns false for a not non-null wrapped type', () => {
329+
expect(
330+
isSemanticNonNullType(
331+
new GraphQLList(new GraphQLSemanticNonNull(ObjectType)),
332+
),
333+
).to.equal(false);
334+
expect(() =>
335+
assertSemanticNonNullType(
336+
new GraphQLList(new GraphQLSemanticNonNull(ObjectType)),
337+
),
338+
).to.throw();
339+
expect(isNonNullType(new GraphQLNonNull(ObjectType))).to.equal(false);
340+
expect(() =>
341+
assertNonNullType(new GraphQLNonNull(ObjectType)),
342+
).to.throw();
301343
});
302344
});
303345

src/type/definition.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,9 @@ export function isInputType(type: unknown): type is GraphQLInputType {
260260
isScalarType(type) ||
261261
isEnumType(type) ||
262262
isInputObjectType(type) ||
263-
(isWrappingType(type) && isInputType(type.ofType))
263+
(!isSemanticNonNullType(type) &&
264+
isWrappingType(type) &&
265+
isInputType(type.ofType))
264266
);
265267
}
266268

@@ -1167,6 +1169,7 @@ export interface GraphQLArgument {
11671169
}
11681170

11691171
export function isRequiredArgument(arg: GraphQLArgument): boolean {
1172+
// Note: input types cannot be SemanticNonNull
11701173
return isNonNullType(arg.type) && arg.defaultValue === undefined;
11711174
}
11721175

@@ -1858,6 +1861,7 @@ export interface GraphQLInputField {
18581861
}
18591862

18601863
export function isRequiredInputField(field: GraphQLInputField): boolean {
1864+
// Note: input types cannot be SemanticNonNull
18611865
return isNonNullType(field.type) && field.defaultValue === undefined;
18621866
}
18631867

src/type/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ export {
2323
isInputObjectType,
2424
isListType,
2525
isNonNullType,
26+
isSemanticNonNullType,
2627
isInputType,
2728
isOutputType,
2829
isLeafType,
@@ -43,6 +44,7 @@ export {
4344
assertInputObjectType,
4445
assertListType,
4546
assertNonNullType,
47+
assertSemanticNonNullType,
4648
assertInputType,
4749
assertOutputType,
4850
assertLeafType,
@@ -64,6 +66,7 @@ export {
6466
// Type Wrappers
6567
GraphQLList,
6668
GraphQLNonNull,
69+
GraphQLSemanticNonNull,
6770
} from './definition';
6871

6972
export type {

src/type/introspection.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ import { print } from '../language/printer';
66

77
import { astFromValue } from '../utilities/astFromValue';
88

9-
import type {
9+
import {
1010
GraphQLEnumValue,
1111
GraphQLField,
1212
GraphQLFieldConfigMap,
1313
GraphQLInputField,
1414
GraphQLNamedType,
1515
GraphQLType,
16+
isSemanticNonNullType,
1617
} from './definition';
1718
import {
1819
GraphQLEnumType,
@@ -237,6 +238,9 @@ export const __Type: GraphQLObjectType = new GraphQLObjectType({
237238
if (isNonNullType(type)) {
238239
return TypeKind.NON_NULL;
239240
}
241+
if (isSemanticNonNullType(type)) {
242+
return TypeKind.SEMANTIC_NON_NULL;
243+
}
240244
/* c8 ignore next 3 */
241245
// Not reachable, all possible types have been considered)
242246
invariant(false, `Unexpected type: "${inspect(type)}".`);
@@ -452,6 +456,7 @@ enum TypeKind {
452456
INPUT_OBJECT = 'INPUT_OBJECT',
453457
LIST = 'LIST',
454458
NON_NULL = 'NON_NULL',
459+
SEMANTIC_NON_NULL = 'SEMANTIC_NON_NULL',
455460
}
456461
export { TypeKind };
457462

src/utilities/astFromValue.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ export function astFromValue(
4242
value: unknown,
4343
type: GraphQLInputType,
4444
): Maybe<ValueNode> {
45+
// Note: input types cannot be SemanticNonNull
4546
if (isNonNullType(type)) {
4647
const astValue = astFromValue(value, type.ofType);
4748
if (astValue?.kind === Kind.NULL) {

0 commit comments

Comments
 (0)