Skip to content

Commit

Permalink
Merge pull request #189 from skevy/wip-subscriptions
Browse files Browse the repository at this point in the history
[RFC] Initial pass at adding `subscription` to executor
  • Loading branch information
leebyron committed Oct 20, 2015
2 parents fbe579d + 87c6a27 commit 39a2ebc
Show file tree
Hide file tree
Showing 22 changed files with 410 additions and 38 deletions.
32 changes: 31 additions & 1 deletion src/execution/__tests__/executor.js
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ describe('Execute: Handles basic execution tasks', () => {
});

it('uses the query schema for queries', async () => {
var doc = `query Q { a } mutation M { c }`;
var doc = `query Q { a } mutation M { c } subscription S { a }`;
var data = { a: 'b', c: 'd' };
var ast = parse(doc);
var schema = new GraphQLSchema({
Expand All @@ -448,6 +448,12 @@ describe('Execute: Handles basic execution tasks', () => {
fields: {
c: { type: GraphQLString },
}
}),
subscription: new GraphQLObjectType({
name: 'S',
fields: {
a: { type: GraphQLString },
}
})
});

Expand Down Expand Up @@ -480,6 +486,30 @@ describe('Execute: Handles basic execution tasks', () => {
expect(mutationResult).to.deep.equal({ data: { c: 'd' } });
});

it('uses the subscription schema for subscriptions', async () => {
var doc = `query Q { a } subscription S { a }`;
var data = { a: 'b', c: 'd' };
var ast = parse(doc);
var schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Q',
fields: {
a: { type: GraphQLString },
}
}),
subscription: new GraphQLObjectType({
name: 'S',
fields: {
a: { type: GraphQLString },
}
})
});

var subscriptionResult = await execute(schema, ast, data, {}, 'S');

expect(subscriptionResult).to.deep.equal({ data: { a: 'b' } });
});

it('correct field ordering despite execution order', async () => {
var doc = `{
a,
Expand Down
11 changes: 10 additions & 1 deletion src/execution/execute.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,9 +240,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 ]
);
}
Expand Down
13 changes: 13 additions & 0 deletions src/language/__tests__/kitchen-sink.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,19 @@ mutation likeStory {
}
}

subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) {
storyLikeSubscribe(input: $input) {
story {
likers {
count
}
likeSentence {
text
}
}
}
}

fragment frag on Friend {
foo(size: $size, bar: $b, obj: {key: "value"})
}
Expand Down
25 changes: 21 additions & 4 deletions src/language/__tests__/parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ fragment MissingOn Type
'fragment',
'query',
'mutation',
'subscription',
'true',
'false'
];
Expand All @@ -185,22 +186,38 @@ fragment ${fragmentName} on Type {
});
});

it('parses experimental subscription feature', () => {
it('parses anonymous mutation operations', () => {
expect(() => parse(`
subscription Foo {
mutation {
mutationField
}
`)).to.not.throw();
});

it('parses anonymous subscription operations', () => {
expect(() => parse(`
subscription {
subscriptionField
}
`)).to.not.throw();
});

it('parses anonymous operations', () => {
it('parses named mutation operations', () => {
expect(() => parse(`
mutation {
mutation Foo {
mutationField
}
`)).to.not.throw();
});

it('parses named subscription operations', () => {
expect(() => parse(`
subscription Foo {
subscriptionField
}
`)).to.not.throw();
});

it('parse creates ast', () => {

var source = new Source(`{
Expand Down
13 changes: 13 additions & 0 deletions src/language/__tests__/printer.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,19 @@ mutation likeStory {
}
}
subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) {
storyLikeSubscribe(input: $input) {
story {
likers {
count
}
likeSentence {
text
}
}
}
}
fragment frag on Friend {
foo(size: $size, bar: $b, obj: {key: "value"})
}
Expand Down
67 changes: 61 additions & 6 deletions src/language/__tests__/visitor.js
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,63 @@ describe('Visitor', () => {
[ 'leave', 'Field', 0, undefined ],
[ 'leave', 'SelectionSet', 'selectionSet', 'OperationDefinition' ],
[ 'leave', 'OperationDefinition', 1, undefined ],
[ 'enter', 'FragmentDefinition', 2, undefined ],
[ 'enter', 'OperationDefinition', 2, undefined ],
[ 'enter', 'Name', 'name', 'OperationDefinition' ],
[ 'leave', 'Name', 'name', 'OperationDefinition' ],
[ 'enter', 'VariableDefinition', 0, undefined ],
[ 'enter', 'Variable', 'variable', 'VariableDefinition' ],
[ 'enter', 'Name', 'name', 'Variable' ],
[ 'leave', 'Name', 'name', 'Variable' ],
[ 'leave', 'Variable', 'variable', 'VariableDefinition' ],
[ 'enter', 'NamedType', 'type', 'VariableDefinition' ],
[ 'enter', 'Name', 'name', 'NamedType' ],
[ 'leave', 'Name', 'name', 'NamedType' ],
[ 'leave', 'NamedType', 'type', 'VariableDefinition' ],
[ 'leave', 'VariableDefinition', 0, undefined ],
[ 'enter', 'SelectionSet', 'selectionSet', 'OperationDefinition' ],
[ 'enter', 'Field', 0, undefined ],
[ 'enter', 'Name', 'name', 'Field' ],
[ 'leave', 'Name', 'name', 'Field' ],
[ 'enter', 'Argument', 0, undefined ],
[ 'enter', 'Name', 'name', 'Argument' ],
[ 'leave', 'Name', 'name', 'Argument' ],
[ 'enter', 'Variable', 'value', 'Argument' ],
[ 'enter', 'Name', 'name', 'Variable' ],
[ 'leave', 'Name', 'name', 'Variable' ],
[ 'leave', 'Variable', 'value', 'Argument' ],
[ 'leave', 'Argument', 0, undefined ],
[ 'enter', 'SelectionSet', 'selectionSet', 'Field' ],
[ 'enter', 'Field', 0, undefined ],
[ 'enter', 'Name', 'name', 'Field' ],
[ 'leave', 'Name', 'name', 'Field' ],
[ 'enter', 'SelectionSet', 'selectionSet', 'Field' ],
[ 'enter', 'Field', 0, undefined ],
[ 'enter', 'Name', 'name', 'Field' ],
[ 'leave', 'Name', 'name', 'Field' ],
[ 'enter', 'SelectionSet', 'selectionSet', 'Field' ],
[ 'enter', 'Field', 0, undefined ],
[ 'enter', 'Name', 'name', 'Field' ],
[ 'leave', 'Name', 'name', 'Field' ],
[ 'leave', 'Field', 0, undefined ],
[ 'leave', 'SelectionSet', 'selectionSet', 'Field' ],
[ 'leave', 'Field', 0, undefined ],
[ 'enter', 'Field', 1, undefined ],
[ 'enter', 'Name', 'name', 'Field' ],
[ 'leave', 'Name', 'name', 'Field' ],
[ 'enter', 'SelectionSet', 'selectionSet', 'Field' ],
[ 'enter', 'Field', 0, undefined ],
[ 'enter', 'Name', 'name', 'Field' ],
[ 'leave', 'Name', 'name', 'Field' ],
[ 'leave', 'Field', 0, undefined ],
[ 'leave', 'SelectionSet', 'selectionSet', 'Field' ],
[ 'leave', 'Field', 1, undefined ],
[ 'leave', 'SelectionSet', 'selectionSet', 'Field' ],
[ 'leave', 'Field', 0, undefined ],
[ 'leave', 'SelectionSet', 'selectionSet', 'Field' ],
[ 'leave', 'Field', 0, undefined ],
[ 'leave', 'SelectionSet', 'selectionSet', 'OperationDefinition' ],
[ 'leave', 'OperationDefinition', 2, undefined ],
[ 'enter', 'FragmentDefinition', 3, undefined ],
[ 'enter', 'Name', 'name', 'FragmentDefinition' ],
[ 'leave', 'Name', 'name', 'FragmentDefinition' ],
[ 'enter', 'NamedType', 'typeCondition', 'FragmentDefinition' ],
Expand Down Expand Up @@ -426,8 +482,8 @@ describe('Visitor', () => {
[ 'leave', 'Argument', 2, undefined ],
[ 'leave', 'Field', 0, undefined ],
[ 'leave', 'SelectionSet', 'selectionSet', 'FragmentDefinition' ],
[ 'leave', 'FragmentDefinition', 2, undefined ],
[ 'enter', 'OperationDefinition', 3, undefined ],
[ 'leave', 'FragmentDefinition', 3, undefined ],
[ 'enter', 'OperationDefinition', 4, undefined ],
[ 'enter', 'SelectionSet', 'selectionSet', 'OperationDefinition' ],
[ 'enter', 'Field', 0, undefined ],
[ 'enter', 'Name', 'name', 'Field' ],
Expand All @@ -450,8 +506,7 @@ describe('Visitor', () => {
[ 'leave', 'Name', 'name', 'Field' ],
[ 'leave', 'Field', 1, undefined ],
[ 'leave', 'SelectionSet', 'selectionSet', 'OperationDefinition' ],
[ 'leave', 'OperationDefinition', 3, undefined ],
[ 'leave', 'Document', undefined, undefined ]
]);
[ 'leave', 'OperationDefinition', 4, undefined ],
[ 'leave', 'Document', undefined, undefined ] ]);
});
});
35 changes: 35 additions & 0 deletions src/type/__tests__/definition.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,16 @@ var BlogMutation = new GraphQLObjectType({
}
});

var BlogSubscription = new GraphQLObjectType({
name: 'Subscription',
fields: {
articleSubscribe: {
args: { id: { type: GraphQLString } },
type: BlogArticle
}
}
});

var ObjectType = new GraphQLObjectType({
name: 'Object',
isTypeOf: () => true
Expand Down Expand Up @@ -143,6 +153,21 @@ describe('Type System: Example', () => {

});

it('defines a subscription schema', () => {
var BlogSchema = new GraphQLSchema({
query: BlogQuery,
subscription: BlogSubscription
});

expect(BlogSchema.getSubscriptionType()).to.equal(BlogSubscription);

var sub = BlogSubscription.getFields()[('articleSubscribe' : string)];
expect(sub && sub.type).to.equal(BlogArticle);
expect(sub && sub.type.name).to.equal('Article');
expect(sub && sub.name).to.equal('articleSubscribe');

});

it('includes nested input objects in the map', () => {
var NestedInputObject = new GraphQLInputObjectType({
name: 'NestedInputObject',
Expand All @@ -161,9 +186,19 @@ describe('Type System: Example', () => {
}
}
});
var SomeSubscription = new GraphQLObjectType({
name: 'SomeSubscription',
fields: {
subscribeToSomething: {
type: BlogArticle,
args: { input: { type: SomeInputObject } }
}
}
});
var schema = new GraphQLSchema({
query: BlogQuery,
mutation: SomeMutation,
subscription: SomeSubscription
});
expect(schema.getTypeMap().NestedInputObject).to.equal(NestedInputObject);
});
Expand Down
32 changes: 31 additions & 1 deletion src/type/__tests__/enumType.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,22 @@ describe('Type System: Enum Values', () => {
}
});

var schema = new GraphQLSchema({ query: QueryType, mutation: MutationType });
var SubscriptionType = new GraphQLObjectType({
name: 'Subscription',
fields: {
subscribeToEnum: {
type: ColorType,
args: { color: { type: ColorType } },
resolve(value, { color }) { return color; }
}
}
});

var schema = new GraphQLSchema({
query: QueryType,
mutation: MutationType,
subscription: SubscriptionType
});

it('accepts enum literals as input', async () => {
expect(
Expand Down Expand Up @@ -173,6 +188,21 @@ describe('Type System: Enum Values', () => {
});
});

it('accepts enum literals as input arguments to subscriptions', async () => {
expect(
await graphql(
schema,
'subscription x($color: Color!) { subscribeToEnum(color: $color) }',
null,
{ color: 'GREEN' }
)
).to.deep.equal({
data: {
subscribeToEnum: 'GREEN'
}
});
});

it('does not accept internal value as enum variable', async () => {
expect(
await graphql(
Expand Down
Loading

0 comments on commit 39a2ebc

Please sign in to comment.