Skip to content

Commit ce28d68

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 2916303 commit ce28d68

10 files changed

+166
-79
lines changed

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

+54-28
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);
163+
}
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+
}
168+
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]);
164173
}
174+
return values[0];
175+
}
165176

166-
assert(!isPromise(value2));
167-
expectJSON(value2).toDeepEqual(value1);
168-
return value1;
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({
@@ -189,19 +213,21 @@ function subscribeWithBadFn(
189213
});
190214
const document = parse('subscription { foo }');
191215

192-
return expectEqualPromisesOrValues(
193-
subscribe({ schema, document }),
216+
return expectEqualPromisesOrValues([
217+
execute({ schema, document }),
194218
createSourceEventStream({ schema, document }),
195-
);
219+
subscribe({ schema, document }),
220+
]);
196221
}
197222

198223
function subscribeWithBadArgs(
199224
args: ExecutionArgs,
200225
): PromiseOrValue<ExecutionResult | AsyncIterable<unknown>> {
201-
return expectEqualPromisesOrValues(
202-
subscribe(args),
226+
return expectEqualPromisesOrValues([
227+
execute(args),
203228
createSourceEventStream(args),
204-
);
229+
subscribe(args),
230+
]);
205231
}
206232

207233
/* eslint-disable @typescript-eslint/require-await */
@@ -223,7 +249,7 @@ describe('Subscription Initialization Phase', () => {
223249
yield { foo: 'FooValue' };
224250
}
225251

226-
const subscription = subscribe({
252+
const subscription = execute({
227253
schema,
228254
document: parse('subscription { foo }'),
229255
rootValue: { foo: fooGenerator },
@@ -259,7 +285,7 @@ describe('Subscription Initialization Phase', () => {
259285
}),
260286
});
261287

262-
const subscription = subscribe({
288+
const subscription = execute({
263289
schema,
264290
document: parse('subscription { foo }'),
265291
});
@@ -297,7 +323,7 @@ describe('Subscription Initialization Phase', () => {
297323
}),
298324
});
299325

300-
const promise = subscribe({
326+
const promise = execute({
301327
schema,
302328
document: parse('subscription { foo }'),
303329
});
@@ -332,7 +358,7 @@ describe('Subscription Initialization Phase', () => {
332358
yield { foo: 'FooValue' };
333359
}
334360

335-
const subscription = subscribe({
361+
const subscription = execute({
336362
schema,
337363
document: parse('subscription { foo }'),
338364
rootValue: { customFoo: fooGenerator },
@@ -382,7 +408,7 @@ describe('Subscription Initialization Phase', () => {
382408
}),
383409
});
384410

385-
const subscription = subscribe({
411+
const subscription = execute({
386412
schema,
387413
document: parse('subscription { foo bar }'),
388414
});
@@ -533,7 +559,7 @@ describe('Subscription Initialization Phase', () => {
533559
}
534560
`);
535561

536-
// If we receive variables that cannot be coerced correctly, subscribe() will
562+
// If we receive variables that cannot be coerced correctly, execute() will
537563
// resolve to an ExecutionResult that contains an informative error description.
538564
const result = subscribeWithBadArgs({ schema, document, variableValues });
539565
expectJSON(result).toDeepEqual({
@@ -948,7 +974,7 @@ describe('Subscription Publish Phase', () => {
948974
});
949975

950976
const document = parse('subscription { newMessage }');
951-
const subscription = subscribe({ schema, document });
977+
const subscription = execute({ schema, document });
952978
assert(isAsyncIterable(subscription));
953979

954980
expect(await subscription.next()).to.deep.equal({
@@ -1009,7 +1035,7 @@ describe('Subscription Publish Phase', () => {
10091035
});
10101036

10111037
const document = parse('subscription { newMessage }');
1012-
const subscription = subscribe({ schema, document });
1038+
const subscription = execute({ schema, document });
10131039
assert(isAsyncIterable(subscription));
10141040

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

0 commit comments

Comments
 (0)