diff --git a/src/execution/execute.js b/src/execution/execute.js index 177d1191a6..51b2f8bcba 100644 --- a/src/execution/execute.js +++ b/src/execution/execute.js @@ -235,9 +235,18 @@ function getOperationRootType( ); } return mutationType; + case 'subscription': + var subscriptionType = schema.getSubscriptionType(); + if (!subscriptionType) { + throw new GraphQLError( + 'Schema is not configured for subscriptions', + [ operation ] + ); + } + return subscriptionType; default: throw new GraphQLError( - 'Can only execute queries and mutations', + 'Can only execute queries, mutations and subscriptions', [ operation ] ); } diff --git a/src/type/__tests__/introspection.js b/src/type/__tests__/introspection.js index ece9afe178..dddff7c2d5 100644 --- a/src/type/__tests__/introspection.js +++ b/src/type/__tests__/introspection.js @@ -1190,6 +1190,11 @@ describe('Introspection', () => { description: 'If this server supports mutation, the type that ' + 'mutation operations will be rooted at.' }, + { + name: 'subscriptionType', + description: 'If this server support subscription, the type ' + + 'that subscription operations will be rooted at.', + }, { name: 'directives', description: 'A list of all directives supported by this server.' diff --git a/src/type/introspection.js b/src/type/introspection.js index 74024faad5..3608b4c4bd 100644 --- a/src/type/introspection.js +++ b/src/type/introspection.js @@ -51,6 +51,12 @@ export var __Schema = new GraphQLObjectType({ type: __Type, resolve: schema => schema.getMutationType() }, + subscriptionType: { + description: 'If this server support subscription, the type that ' + + 'subscription operations will be rooted at.', + type: __Type, + resolve: schema => schema.getSubscriptionType() + }, directives: { description: 'A list of all directives supported by this server.', type: diff --git a/src/type/schema.js b/src/type/schema.js index 4265e62e05..565976771d 100644 --- a/src/type/schema.js +++ b/src/type/schema.js @@ -58,12 +58,18 @@ export class GraphQLSchema { `Schema mutation must be Object Type if provided but ` + `got: ${config.mutation}.` ); + invariant( + !config.subscription || config.subscription instanceof GraphQLObjectType, + `Schema subscription must be Object Type if provided but ` + + `got: ${config.subscription}.` + ); this._schemaConfig = config; // Build type map now to detect any errors within this schema. this._typeMap = [ this.getQueryType(), this.getMutationType(), + this.getSubscriptionType(), __Schema ].reduce(typeMapReducer, {}); @@ -86,6 +92,10 @@ export class GraphQLSchema { return this._schemaConfig.mutation; } + getSubscriptionType(): ?GraphQLObjectType { + return this._schemaConfig.subscription; + } + getTypeMap(): TypeMap { return this._typeMap; } @@ -111,6 +121,7 @@ type TypeMap = { [typeName: string]: GraphQLType } type GraphQLSchemaConfig = { query: GraphQLObjectType; mutation?: ?GraphQLObjectType; + subscription?: ?GraphQLObjectType; } function typeMapReducer(map: TypeMap, type: ?GraphQLType): TypeMap { diff --git a/src/utilities/TypeInfo.js b/src/utilities/TypeInfo.js index 694615cae7..fed5ac3b59 100644 --- a/src/utilities/TypeInfo.js +++ b/src/utilities/TypeInfo.js @@ -127,6 +127,8 @@ export class TypeInfo { type = schema.getQueryType(); } else if (node.operation === 'mutation') { type = schema.getMutationType(); + } else if (node.operation === 'subscription') { + type = schema.getSubscriptionType(); } this._typeStack.push(type); break; diff --git a/src/utilities/__tests__/schemaPrinter.js b/src/utilities/__tests__/schemaPrinter.js index c1d50d4a38..790f335cc0 100644 --- a/src/utilities/__tests__/schemaPrinter.js +++ b/src/utilities/__tests__/schemaPrinter.js @@ -544,6 +544,7 @@ type __Schema { types: [__Type!]! queryType: __Type! mutationType: __Type + subscriptionType: __Type directives: [__Directive!]! } diff --git a/src/utilities/buildASTSchema.js b/src/utilities/buildASTSchema.js index 65de287ad4..c1d834d279 100644 --- a/src/utilities/buildASTSchema.js +++ b/src/utilities/buildASTSchema.js @@ -88,7 +88,8 @@ function getInnerTypeName(typeAST) { export function buildASTSchema( ast: Document, queryTypeName: string, - mutationTypeName: ?string + mutationTypeName: ?string, + subscriptionTypeName: ?string ): GraphQLSchema { if (isNullish(ast)) { @@ -121,6 +122,12 @@ export function buildASTSchema( ' not found in document.'); } + if (!isNullish(subscriptionTypeName) && + isNullish(astMap[subscriptionTypeName])) { + throw new Error('Specified subscription type ' + subscriptionTypeName + + ' not found in document.'); + } + /** * This generates a function that allows you to produce * type definitions on demand. We produce the function @@ -161,16 +168,20 @@ export function buildASTSchema( ast.definitions.forEach(produceTypeDef); var queryType = produceTypeDef(astMap[queryTypeName]); - var schema; - if (isNullish(mutationTypeName)) { - schema = new GraphQLSchema({ query: queryType }); - } else { - schema = new GraphQLSchema({ - query: queryType, - mutation: produceTypeDef(astMap[mutationTypeName]), - }); + + var schemaBody = { + query: queryType + }; + + if (!isNullish(mutationTypeName)) { + schemaBody.mutation = produceTypeDef(astMap[mutationTypeName]); + } + + if (!isNullish(subscriptionTypeName)) { + schemaBody.subscription = produceTypeDef(astMap[subscriptionTypeName]); } + var schema = new GraphQLSchema(schemaBody); return schema; function makeSchemaDef(def) { diff --git a/src/utilities/buildClientSchema.js b/src/utilities/buildClientSchema.js index 779d92396f..cbfefa47b0 100644 --- a/src/utilities/buildClientSchema.js +++ b/src/utilities/buildClientSchema.js @@ -312,16 +312,20 @@ export function buildClientSchema( typeIntrospection => getNamedType(typeIntrospection.name) ); - // Get the root Query and Mutation types. + // Get the root Query, Mutation, and Subscription types. var queryType = getType(schemaIntrospection.queryType); var mutationType = schemaIntrospection.mutationType ? getType(schemaIntrospection.mutationType) : null; + var subscriptionType = schemaIntrospection.subscriptionType ? + getType(schemaIntrospection.subscriptionType) : + null; // Then produce and return a Schema with these types. var schema = new GraphQLSchema({ query: (queryType: any), - mutation: (mutationType: any) + mutation: (mutationType: any), + subscription: (subscriptionType: any) }); return schema; diff --git a/src/utilities/introspectionQuery.js b/src/utilities/introspectionQuery.js index e67862ec5c..336a3fe762 100644 --- a/src/utilities/introspectionQuery.js +++ b/src/utilities/introspectionQuery.js @@ -13,6 +13,7 @@ export var introspectionQuery = ` __schema { queryType { name } mutationType { name } + subscriptionType { name } types { ...FullType } @@ -94,6 +95,7 @@ export type IntrospectionQuery = { export type IntrospectionSchema = { queryType: IntrospectionNamedTypeRef; mutationType: ?IntrospectionNamedTypeRef; + subscriptionType: ?IntrospectionNamedTypeRef; types: Array; directives: Array; }