Skip to content

Commit cbd99b5

Browse files
committed
execute: integrate subscriptions
`execute` no longer runs the query algorithm for subscription operations. Rather, subscription operations are performed, as per the spec. `subscribe` is deprecated.
1 parent 9df534c commit cbd99b5

11 files changed

+187
-77
lines changed

integrationTests/ts/basic-test.ts

+6-5
Original file line numberDiff line numberDiff line change
@@ -23,12 +23,13 @@ const queryType: GraphQLObjectType = new GraphQLObjectType({
2323

2424
const schema: GraphQLSchema = new GraphQLSchema({ query: queryType });
2525

26-
const result: ExecutionResult = graphqlSync({
27-
schema,
28-
source: `
26+
const result: ExecutionResult | AsyncGenerator<ExecutionResult, void, void> =
27+
graphqlSync({
28+
schema,
29+
source: `
2930
query helloWho($who: String){
3031
test(who: $who)
3132
}
3233
`,
33-
variableValues: { who: 'Dolly' },
34-
});
34+
variableValues: { who: 'Dolly' },
35+
});

src/__tests__/starWarsIntrospection-test.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
import { expect } from 'chai';
1+
import { assert, expect } from 'chai';
22
import { describe, it } from 'mocha';
33

4+
import { isAsyncIterable } from '../jsutils/isAsyncIterable';
5+
46
import { graphqlSync } from '../graphql';
57

68
import { StarWarsSchema } from './starWarsSchema';
79

810
function queryStarWars(source: string) {
911
const result = graphqlSync({ schema: StarWarsSchema, source });
1012
expect(Object.keys(result)).to.deep.equal(['data']);
13+
assert(!isAsyncIterable(result));
1114
return result.data;
1215
}
1316

src/execution/__tests__/executor-test.ts

+15-4
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { describe, it } from 'mocha';
44
import { expectJSON } from '../../__testUtils__/expectJSON';
55

66
import { inspect } from '../../jsutils/inspect';
7+
import { isAsyncIterable } from '../../jsutils/isAsyncIterable';
78

89
import { Kind } from '../../language/kinds';
910
import { parse } from '../../language/parser';
@@ -833,7 +834,7 @@ describe('Execute: Handles basic execution tasks', () => {
833834
expect(result).to.deep.equal({ data: { c: 'd' } });
834835
});
835836

836-
it('uses the subscription schema for subscriptions', () => {
837+
it('uses the subscription schema for subscriptions', async () => {
837838
const schema = new GraphQLSchema({
838839
query: new GraphQLObjectType({
839840
name: 'Q',
@@ -852,11 +853,22 @@ describe('Execute: Handles basic execution tasks', () => {
852853
query Q { a }
853854
subscription S { a }
854855
`);
855-
const rootValue = { a: 'b', c: 'd' };
856+
const rootValue = {
857+
// eslint-disable-next-line @typescript-eslint/require-await
858+
async *a() {
859+
yield { a: 'b' }; /* c8 ignore start */
860+
} /* c8 ignore stop */,
861+
c: 'd',
862+
};
856863
const operationName = 'S';
857864

858865
const result = executeSync({ schema, document, rootValue, operationName });
859-
expect(result).to.deep.equal({ data: { a: 'b' } });
866+
867+
assert(isAsyncIterable(result));
868+
expect(await result.next()).to.deep.equal({
869+
value: { data: { a: 'b' } },
870+
done: false,
871+
});
860872
});
861873

862874
it('resolves to an error if schema does not support operation', () => {
@@ -895,7 +907,6 @@ describe('Execute: Handles basic execution tasks', () => {
895907
expectJSON(
896908
executeSync({ schema, document, operationName: 'S' }),
897909
).toDeepEqual({
898-
data: null,
899910
errors: [
900911
{
901912
message:

src/execution/__tests__/nonnull-test.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1-
import { expect } from 'chai';
1+
import { assert, expect } from 'chai';
22
import { describe, it } from 'mocha';
33

44
import { expectJSON } from '../../__testUtils__/expectJSON';
55

6+
import { isAsyncIterable } from '../../jsutils/isAsyncIterable';
7+
import type { PromiseOrValue } from '../../jsutils/PromiseOrValue';
8+
69
import { parse } from '../../language/parser';
710

811
import { GraphQLNonNull, GraphQLObjectType } from '../../type/definition';
@@ -109,7 +112,9 @@ const schema = buildSchema(`
109112
function executeQuery(
110113
query: string,
111114
rootValue: unknown,
112-
): ExecutionResult | Promise<ExecutionResult> {
115+
): PromiseOrValue<
116+
ExecutionResult | AsyncGenerator<ExecutionResult, void, void>
117+
> {
113118
return execute({ schema, document: parse(query), rootValue });
114119
}
115120

@@ -132,6 +137,7 @@ async function executeSyncAndAsync(query: string, rootValue: unknown) {
132137
rootValue,
133138
});
134139

140+
assert(!isAsyncIterable(syncResult));
135141
expectJSON(asyncResult).toDeepEqual(patchData(syncResult));
136142
return syncResult;
137143
}

src/execution/__tests__/subscribe-test.ts

+50-25
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import { GraphQLBoolean, GraphQLInt, GraphQLString } from '../../type/scalars';
1515
import { GraphQLSchema } from '../../type/schema';
1616

1717
import type { ExecutionArgs, ExecutionResult } from '../execute';
18-
import { createSourceEventStream, subscribe } from '../execute';
18+
import { createSourceEventStream, execute, subscribe } from '../execute';
1919

2020
import { SimplePubSub } from './simplePubSub';
2121

@@ -122,7 +122,7 @@ function createSubscription(pubsub: SimplePubSub<Email>) {
122122
}),
123123
};
124124

125-
return subscribe({ schema: emailSchema, document, rootValue: data });
125+
return execute({ schema: emailSchema, document, rootValue: data });
126126
}
127127

128128
// TODO: consider adding this method to testUtils (with tests)
@@ -150,22 +150,46 @@ function expectPromise(maybePromise: unknown) {
150150
};
151151
}
152152

153-
// TODO: consider adding this method to testUtils (with tests)
153+
// TODO: consider adding these method to testUtils (with tests)
154154
function expectEqualPromisesOrValues<T>(
155-
value1: PromiseOrValue<T>,
156-
value2: PromiseOrValue<T>,
155+
items: ReadonlyArray<PromiseOrValue<T>>,
157156
): PromiseOrValue<T> {
158-
if (isPromise(value1)) {
159-
assert(isPromise(value2));
160-
return Promise.all([value1, value2]).then((resolved) => {
161-
expectJSON(resolved[1]).toDeepEqual(resolved[0]);
162-
return resolved[0];
163-
});
157+
if (isPromise(items[0])) {
158+
if (assertAllPromises(items)) {
159+
return Promise.all(items).then(expectMatchingValues);
160+
}
161+
} else if (assertNoPromises(items)) {
162+
return expectMatchingValues(items);
164163
}
164+
/* c8 ignore next 3 */
165+
// Not reachable, all possible output types have been considered.
166+
assert(false, 'Receives mixture of promises and values.');
167+
}
165168

166-
assert(!isPromise(value2));
167-
expectJSON(value2).toDeepEqual(value1);
168-
return value1;
169+
function expectMatchingValues<T>(values: ReadonlyArray<T>): T {
170+
const remainingValues = values.slice(1);
171+
for (const value of remainingValues) {
172+
expectJSON(value).toDeepEqual(values[0]);
173+
}
174+
return values[0];
175+
}
176+
177+
function assertAllPromises<T>(
178+
items: ReadonlyArray<PromiseOrValue<T>>,
179+
): items is ReadonlyArray<Promise<T>> {
180+
for (const item of items) {
181+
assert(isPromise(item));
182+
}
183+
return true;
184+
}
185+
186+
function assertNoPromises<T>(
187+
items: ReadonlyArray<PromiseOrValue<T>>,
188+
): items is ReadonlyArray<T> {
189+
for (const item of items) {
190+
assert(!isPromise(item));
191+
}
192+
return true;
169193
}
170194

171195
const DummyQueryType = new GraphQLObjectType({
@@ -195,10 +219,11 @@ function subscribeWithBadFn(
195219
function subscribeWithBadArgs(
196220
args: ExecutionArgs,
197221
): PromiseOrValue<ExecutionResult | AsyncIterable<unknown>> {
198-
return expectEqualPromisesOrValues(
199-
subscribe(args),
222+
return expectEqualPromisesOrValues([
223+
execute(args),
200224
createSourceEventStream(args),
201-
);
225+
subscribe(args),
226+
]);
202227
}
203228

204229
/* eslint-disable @typescript-eslint/require-await */
@@ -220,7 +245,7 @@ describe('Subscription Initialization Phase', () => {
220245
yield { foo: 'FooValue' };
221246
}
222247

223-
const subscription = subscribe({
248+
const subscription = execute({
224249
schema,
225250
document: parse('subscription { foo }'),
226251
rootValue: { foo: fooGenerator },
@@ -256,7 +281,7 @@ describe('Subscription Initialization Phase', () => {
256281
}),
257282
});
258283

259-
const subscription = subscribe({
284+
const subscription = execute({
260285
schema,
261286
document: parse('subscription { foo }'),
262287
});
@@ -294,7 +319,7 @@ describe('Subscription Initialization Phase', () => {
294319
}),
295320
});
296321

297-
const promise = subscribe({
322+
const promise = execute({
298323
schema,
299324
document: parse('subscription { foo }'),
300325
});
@@ -329,7 +354,7 @@ describe('Subscription Initialization Phase', () => {
329354
yield { foo: 'FooValue' };
330355
}
331356

332-
const subscription = subscribe({
357+
const subscription = execute({
333358
schema,
334359
document: parse('subscription { foo }'),
335360
rootValue: { customFoo: fooGenerator },
@@ -379,7 +404,7 @@ describe('Subscription Initialization Phase', () => {
379404
}),
380405
});
381406

382-
const subscription = subscribe({
407+
const subscription = execute({
383408
schema,
384409
document: parse('subscription { foo bar }'),
385410
});
@@ -530,7 +555,7 @@ describe('Subscription Initialization Phase', () => {
530555
}
531556
`);
532557

533-
// If we receive variables that cannot be coerced correctly, subscribe() will
558+
// If we receive variables that cannot be coerced correctly, execute() will
534559
// resolve to an ExecutionResult that contains an informative error description.
535560
const result = subscribeWithBadArgs({ schema, document, variableValues });
536561
expectJSON(result).toDeepEqual({
@@ -945,7 +970,7 @@ describe('Subscription Publish Phase', () => {
945970
});
946971

947972
const document = parse('subscription { newMessage }');
948-
const subscription = subscribe({ schema, document });
973+
const subscription = execute({ schema, document });
949974
assert(isAsyncIterable(subscription));
950975

951976
expect(await subscription.next()).to.deep.equal({
@@ -1006,7 +1031,7 @@ describe('Subscription Publish Phase', () => {
10061031
});
10071032

10081033
const document = parse('subscription { newMessage }');
1009-
const subscription = subscribe({ schema, document });
1034+
const subscription = execute({ schema, document });
10101035
assert(isAsyncIterable(subscription));
10111036

10121037
expect(await subscription.next()).to.deep.equal({

0 commit comments

Comments
 (0)