Skip to content

Commit 379a308

Browse files
committed
FIX: Uphold spec for non-validation names not beginning with __
The spec describing introspection (http://facebook.github.io/graphql/#sec-Naming-conventions) restricts naming non-introspection related artifacts starting with __. This enforces that specification.
1 parent 9d359b8 commit 379a308

File tree

4 files changed

+47
-17
lines changed

4 files changed

+47
-17
lines changed

src/type/__tests__/validation-test.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,17 @@ describe('Type System: Objects must have fields', () => {
341341
);
342342
});
343343

344+
it('rejects an Object type with reserved named fields', () => {
345+
expect(
346+
() => schemaWithFieldType(new GraphQLObjectType({
347+
name: 'SomeObject',
348+
fields: { __notPartOfIntrospection: { type: GraphQLString } }
349+
}))
350+
).to.throw(
351+
'Name "__notPartOfIntrospection" must not begin with "__", which is reserved by GraphQL introspection.'
352+
);
353+
});
354+
344355
it('rejects an Object type with incorrectly typed fields', () => {
345356
expect(
346357
() => schemaWithFieldType(new GraphQLObjectType({

src/type/definition.js

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -291,7 +291,6 @@ export class GraphQLScalarType {
291291
_scalarConfig: GraphQLScalarTypeConfig<*, *>;
292292

293293
constructor(config: GraphQLScalarTypeConfig<*, *>) {
294-
invariant(config.name, 'Type must be named.');
295294
assertValidName(config.name);
296295
this.name = config.name;
297296
this.description = config.description;
@@ -400,8 +399,7 @@ export class GraphQLObjectType {
400399
_interfaces: Array<GraphQLInterfaceType>;
401400

402401
constructor(config: GraphQLObjectTypeConfig<*, *>) {
403-
invariant(config.name, 'Type must be named.');
404-
assertValidName(config.name);
402+
assertValidName(config.name, config.isIntrospection);
405403
this.name = config.name;
406404
this.description = config.description;
407405
if (config.isTypeOf) {
@@ -547,7 +545,8 @@ export type GraphQLObjectTypeConfig<TSource, TContext> = {
547545
interfaces?: Thunk<?Array<GraphQLInterfaceType>>;
548546
fields: Thunk<GraphQLFieldConfigMap<TSource, TContext>>;
549547
isTypeOf?: ?GraphQLIsTypeOfFn<TSource, TContext>;
550-
description?: ?string
548+
description?: ?string;
549+
isIntrospection?: boolean;
551550
};
552551

553552
export type GraphQLTypeResolver<TSource, TContext> = (
@@ -656,7 +655,6 @@ export class GraphQLInterfaceType {
656655
_fields: GraphQLFieldMap<*, *>;
657656

658657
constructor(config: GraphQLInterfaceTypeConfig<*, *>) {
659-
invariant(config.name, 'Type must be named.');
660658
assertValidName(config.name);
661659
this.name = config.name;
662660
this.description = config.description;
@@ -735,7 +733,6 @@ export class GraphQLUnionType {
735733
_possibleTypeNames: {[typeName: string]: boolean};
736734

737735
constructor(config: GraphQLUnionTypeConfig<*, *>) {
738-
invariant(config.name, 'Type must be named.');
739736
assertValidName(config.name);
740737
this.name = config.name;
741738
this.description = config.description;
@@ -845,7 +842,7 @@ export class GraphQLEnumType/* <T> */ {
845842

846843
constructor(config: GraphQLEnumTypeConfig/* <T> */) {
847844
this.name = config.name;
848-
assertValidName(config.name);
845+
assertValidName(config.name, config.isIntrospection);
849846
this.description = config.description;
850847
this._values = defineEnumValues(this, config.values);
851848
this._enumConfig = config;
@@ -953,6 +950,7 @@ export type GraphQLEnumTypeConfig/* <T> */ = {
953950
name: string;
954951
values: GraphQLEnumValueConfigMap/* <T> */;
955952
description?: ?string;
953+
isIntrospection?: boolean;
956954
};
957955

958956
export type GraphQLEnumValueConfigMap/* <T> */ = {
@@ -1003,7 +1001,6 @@ export class GraphQLInputObjectType {
10031001
_fields: GraphQLInputFieldMap;
10041002

10051003
constructor(config: GraphQLInputObjectTypeConfig) {
1006-
invariant(config.name, 'Type must be named.');
10071004
assertValidName(config.name);
10081005
this.name = config.name;
10091006
this.description = config.description;

src/type/introspection.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import type { GraphQLField } from './definition';
2828

2929
export const __Schema = new GraphQLObjectType({
3030
name: '__Schema',
31+
isIntrospection: true,
3132
description:
3233
'A GraphQL Schema defines the capabilities of a GraphQL server. It ' +
3334
'exposes all available types and directives on the server, as well as ' +
@@ -69,6 +70,7 @@ export const __Schema = new GraphQLObjectType({
6970

7071
export const __Directive = new GraphQLObjectType({
7172
name: '__Directive',
73+
isIntrospection: true,
7274
description:
7375
'A Directive provides a way to describe alternate runtime execution and ' +
7476
'type validation behavior in a GraphQL document.' +
@@ -117,6 +119,7 @@ export const __Directive = new GraphQLObjectType({
117119

118120
export const __DirectiveLocation = new GraphQLEnumType({
119121
name: '__DirectiveLocation',
122+
isIntrospection: true,
120123
description:
121124
'A Directive can be adjacent to many parts of the GraphQL language, a ' +
122125
'__DirectiveLocation describes one such possible adjacencies.',
@@ -198,6 +201,7 @@ export const __DirectiveLocation = new GraphQLEnumType({
198201

199202
export const __Type = new GraphQLObjectType({
200203
name: '__Type',
204+
isIntrospection: true,
201205
description:
202206
'The fundamental unit of any GraphQL Schema is the type. There are ' +
203207
'many kinds of types in GraphQL as represented by the `__TypeKind` enum.' +
@@ -299,6 +303,7 @@ export const __Type = new GraphQLObjectType({
299303

300304
export const __Field = new GraphQLObjectType({
301305
name: '__Field',
306+
isIntrospection: true,
302307
description:
303308
'Object and Interface types are described by a list of Fields, each of ' +
304309
'which has a name, potentially a list of arguments, and a return type.',
@@ -320,6 +325,7 @@ export const __Field = new GraphQLObjectType({
320325

321326
export const __InputValue = new GraphQLObjectType({
322327
name: '__InputValue',
328+
isIntrospection: true,
323329
description:
324330
'Arguments provided to Fields or Directives and the input fields of an ' +
325331
'InputObject are represented as Input Values which describe their type ' +
@@ -342,6 +348,7 @@ export const __InputValue = new GraphQLObjectType({
342348

343349
export const __EnumValue = new GraphQLObjectType({
344350
name: '__EnumValue',
351+
isIntrospection: true,
345352
description:
346353
'One possible value for a given Enum. Enum values are unique values, not ' +
347354
'a placeholder for a string or numeric value. However an Enum value is ' +
@@ -369,6 +376,7 @@ export const TypeKind = {
369376

370377
export const __TypeKind = new GraphQLEnumType({
371378
name: '__TypeKind',
379+
isIntrospection: true,
372380
description: 'An enum describing what kind of type a given `__Type` is.',
373381
values: {
374382
SCALAR: {

src/utilities/assertValidName.js

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,29 @@
88
* of patent rights can be found in the PATENTS file in the same directory.
99
*/
1010

11-
import invariant from '../jsutils/invariant';
12-
13-
1411
const NAME_RX = /^[_a-zA-Z][_a-zA-Z0-9]*$/;
1512

16-
// Helper to assert that provided names are valid.
17-
export function assertValidName(name: string): void {
18-
invariant(
19-
NAME_RX.test(name),
20-
`Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "${name}" does not.`
21-
);
13+
/**
14+
* Upholds the spec rules about naming.
15+
*/
16+
export function assertValidName(
17+
name: string,
18+
isIntrospection?: boolean
19+
): void {
20+
if (!name || typeof name !== 'string') {
21+
throw new Error(
22+
`Must be named. Unexpected name: ${name}.`
23+
);
24+
}
25+
if (!isIntrospection && name.slice(0, 2) === '__') {
26+
throw new Error(
27+
`Name "${name}" must not begin with "__", which is reserved by ` +
28+
'GraphQL introspection.'
29+
);
30+
}
31+
if (!NAME_RX.test(name)) {
32+
throw new Error(
33+
`Names must match /^[_a-zA-Z][_a-zA-Z0-9]*$/ but "${name}" does not.`
34+
);
35+
}
2236
}

0 commit comments

Comments
 (0)