Skip to content

Commit 25928e3

Browse files
committed
Validate oneOf types
This validates that all fields of oneOf objects are nullable and that all fields of oneOf input object are nullable and do not have a default value.
1 parent 534b243 commit 25928e3

File tree

2 files changed

+106
-1
lines changed

2 files changed

+106
-1
lines changed

src/type/__tests__/validation-test.ts

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -336,7 +336,7 @@ describe('Type System: A Schema must have Object root types', () => {
336336
]);
337337
});
338338

339-
it('rejects a schema extended with invalid root types', () => {
339+
it('rejects a Schema extended with invalid root types', () => {
340340
let schema = buildSchema(`
341341
input SomeInputObject {
342342
test: String
@@ -1663,6 +1663,69 @@ describe('Type System: Input Object fields must have input types', () => {
16631663
});
16641664
});
16651665

1666+
describe('Type System: OneOf Object fields must be nullable', () => {
1667+
it('rejects non-nullable fields', () => {
1668+
const schema = buildSchema(`
1669+
type Query {
1670+
test: SomeObject
1671+
}
1672+
1673+
type SomeObject @oneOf {
1674+
a: String
1675+
b: String!
1676+
}
1677+
`);
1678+
expectJSON(validateSchema(schema)).toDeepEqual([
1679+
{
1680+
message:
1681+
'Field SomeObject.b must be nullable as it is part of a OneOf Type.',
1682+
locations: [{ line: 8, column: 12 }],
1683+
},
1684+
]);
1685+
});
1686+
});
1687+
1688+
describe('Type System: OneOf Input Object fields must be nullable', () => {
1689+
it('rejects non-nullable fields', () => {
1690+
const schema = buildSchema(`
1691+
type Query {
1692+
test(arg: SomeInputObject): String
1693+
}
1694+
1695+
input SomeInputObject @oneOf {
1696+
a: String
1697+
b: String!
1698+
}
1699+
`);
1700+
expectJSON(validateSchema(schema)).toDeepEqual([
1701+
{
1702+
message: 'OneOf input field SomeInputObject.b must be nullable.',
1703+
locations: [{ line: 8, column: 12 }],
1704+
},
1705+
]);
1706+
});
1707+
1708+
it('rejects fields with default values', () => {
1709+
const schema = buildSchema(`
1710+
type Query {
1711+
test(arg: SomeInputObject): String
1712+
}
1713+
1714+
input SomeInputObject @oneOf {
1715+
a: String
1716+
b: String = "foo"
1717+
}
1718+
`);
1719+
expectJSON(validateSchema(schema)).toDeepEqual([
1720+
{
1721+
message:
1722+
'OneOf input field SomeInputObject.b cannot have a default value.',
1723+
locations: [{ line: 8, column: 9 }],
1724+
},
1725+
]);
1726+
});
1727+
});
1728+
16661729
describe('Objects must adhere to Interface they implement', () => {
16671730
it('accepts an Object which implements an Interface', () => {
16681731
const schema = buildSchema(`

src/type/validate.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { isEqualType, isTypeSubTypeOf } from '../utilities/typeComparators';
2020

2121
import type {
2222
GraphQLEnumType,
23+
GraphQLField,
2324
GraphQLInputField,
2425
GraphQLInputObjectType,
2526
GraphQLInterfaceType,
@@ -308,6 +309,23 @@ function validateFields(
308309
);
309310
}
310311
}
312+
313+
if (isObjectType(type) && type.isOneOf) {
314+
validateOneOfObjectField(type, field, context);
315+
}
316+
}
317+
}
318+
319+
function validateOneOfObjectField(
320+
type: GraphQLObjectType,
321+
field: GraphQLField<unknown, unknown, unknown>,
322+
context: SchemaValidationContext,
323+
): void {
324+
if (isNonNullType(field.type)) {
325+
context.reportError(
326+
`Field ${type.name}.${field.name} must be nullable as it is part of a OneOf Type.`,
327+
field.astNode?.type,
328+
);
311329
}
312330
}
313331

@@ -531,6 +549,30 @@ function validateInputFields(
531549
[getDeprecatedDirectiveNode(field.astNode), field.astNode?.type],
532550
);
533551
}
552+
553+
if (inputObj.isOneOf) {
554+
validateOneOfInputObjectField(inputObj, field, context);
555+
}
556+
}
557+
}
558+
559+
function validateOneOfInputObjectField(
560+
type: GraphQLInputObjectType,
561+
field: GraphQLInputField,
562+
context: SchemaValidationContext,
563+
): void {
564+
if (isNonNullType(field.type)) {
565+
context.reportError(
566+
`OneOf input field ${type.name}.${field.name} must be nullable.`,
567+
field.astNode?.type,
568+
);
569+
}
570+
571+
if (field.defaultValue) {
572+
context.reportError(
573+
`OneOf input field ${type.name}.${field.name} cannot have a default value.`,
574+
field.astNode,
575+
);
534576
}
535577
}
536578

0 commit comments

Comments
 (0)