From 2d123cec665dcb001a346626d9991efa8b57d253 Mon Sep 17 00:00:00 2001 From: Laurin Quast Date: Mon, 21 Sep 2020 18:41:39 +0200 Subject: [PATCH 1/5] fix(typings): return AsyncIterableIterator instead of AsyncIterator BREAKING fixes the type annotation of the abstract class PubSubEngine. According to the TypeScript type-defintion a `PubSubAsyncIterator` instance is actually a `AsyncIterableIterator` instead of an `AsyncIterator`. The typing of `PubSubAsyncIterator` is way more convenient as it allows iterating over it with the `for await (const foo of iterator) { doSth() }` syntax, which is super handy for filtering or mapping (See https://gist.github.com/n1ru4l/127178705cc0942cad0e45d425e2eb63 for some example operators). --- src/pubsub-engine.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pubsub-engine.ts b/src/pubsub-engine.ts index afe18d7..66f0ced 100644 --- a/src/pubsub-engine.ts +++ b/src/pubsub-engine.ts @@ -4,7 +4,7 @@ export abstract class PubSubEngine { public abstract publish(triggerName: string, payload: any): Promise; public abstract subscribe(triggerName: string, onMessage: Function, options: Object): Promise; public abstract unsubscribe(subId: number); - public asyncIterator(triggers: string | string[]): AsyncIterator { + public asyncIterator(triggers: string | string[]): AsyncIterableIterator { return new PubSubAsyncIterator(this, triggers); } } From 4b1096a6a030e51796fcdc422b116e1584d3367a Mon Sep 17 00:00:00 2001 From: n1ru4l Date: Mon, 21 Sep 2020 22:47:53 +0200 Subject: [PATCH 2/5] remove iterall --- package.json | 4 +-- ...r.ts => pubsub-async-iterable-iterator.ts} | 6 ++-- src/pubsub-engine.ts | 6 ++-- src/test/asyncIteratorSubscription.ts | 31 ++++++++++++++----- src/test/tests.ts | 7 +++-- src/with-filter.ts | 7 ++--- 6 files changed, 37 insertions(+), 24 deletions(-) rename src/{pubsub-async-iterator.ts => pubsub-async-iterable-iterator.ts} (96%) diff --git a/package.json b/package.json index 57596f1..124685c 100644 --- a/package.json +++ b/package.json @@ -7,9 +7,7 @@ "type": "git", "url": "https://github.com/apollostack/graphql-subscriptions.git" }, - "dependencies": { - "iterall": "^1.3.0" - }, + "dependencies": {}, "peerDependencies": { "graphql": "^15.7.2 || ^16.0.0" }, diff --git a/src/pubsub-async-iterator.ts b/src/pubsub-async-iterable-iterator.ts similarity index 96% rename from src/pubsub-async-iterator.ts rename to src/pubsub-async-iterable-iterator.ts index 31bca41..9284902 100644 --- a/src/pubsub-async-iterator.ts +++ b/src/pubsub-async-iterable-iterator.ts @@ -1,4 +1,3 @@ -import { $$asyncIterator } from 'iterall'; import { PubSubEngine } from './pubsub-engine'; /** @@ -33,7 +32,7 @@ import { PubSubEngine } from './pubsub-engine'; * @property pubsub @type {PubSubEngine} * The PubSubEngine whose events will be observed. */ -export class PubSubAsyncIterator implements AsyncIterator { +export class PubSubAsyncIterableIterator implements AsyncIterableIterator { private pullQueue: ((value: IteratorResult) => void)[]; private pushQueue: T[]; @@ -66,7 +65,7 @@ export class PubSubAsyncIterator implements AsyncIterator { return Promise.reject(error); } - public [$$asyncIterator]() { + public [Symbol.asyncIterator]() { return this; } @@ -119,5 +118,4 @@ export class PubSubAsyncIterator implements AsyncIterator { this.pubsub.unsubscribe(subscriptionId); } } - } diff --git a/src/pubsub-engine.ts b/src/pubsub-engine.ts index 66f0ced..a6478da 100644 --- a/src/pubsub-engine.ts +++ b/src/pubsub-engine.ts @@ -1,10 +1,10 @@ -import {PubSubAsyncIterator} from './pubsub-async-iterator'; +import {PubSubAsyncIterableIterator} from './pubsub-async-iterable-iterator'; export abstract class PubSubEngine { public abstract publish(triggerName: string, payload: any): Promise; public abstract subscribe(triggerName: string, onMessage: Function, options: Object): Promise; public abstract unsubscribe(subId: number); - public asyncIterator(triggers: string | string[]): AsyncIterableIterator { - return new PubSubAsyncIterator(this, triggers); + public asyncIterator(triggers: string | string[]): PubSubAsyncIterableIterator { + return new PubSubAsyncIterableIterator(this, triggers); } } diff --git a/src/test/asyncIteratorSubscription.ts b/src/test/asyncIteratorSubscription.ts index 81d98d4..a856ae8 100644 --- a/src/test/asyncIteratorSubscription.ts +++ b/src/test/asyncIteratorSubscription.ts @@ -5,11 +5,14 @@ import * as chaiAsPromised from 'chai-as-promised'; import { spy } from 'sinon'; import * as sinonChai from 'sinon-chai'; -import { createAsyncIterator, isAsyncIterable } from 'iterall'; import { PubSub } from '../pubsub'; import { withFilter, FilterFn } from '../with-filter'; import { ExecutionResult } from 'graphql'; +const isAsyncIterableIterator = (input: unknown): input is AsyncIterableIterator => { + return input != null && typeof input[Symbol.asyncIterator] === 'function'; +}; + chai.use(chaiAsPromised); chai.use(sinonChai); const expect = chai.expect; @@ -67,11 +70,10 @@ describe('GraphQL-JS asyncIterator', () => { const origIterator = pubsub.asyncIterator(FIRST_EVENT); const schema = buildSchema(origIterator); - - const results = await subscribe({schema, document: query}) as AsyncIterator; + const results = await subscribe({ schema, document: query }) as AsyncIterableIterator; const payload1 = results.next(); - expect(isAsyncIterable(results)).to.be.true; + expect(isAsyncIterableIterator(results)).to.be.true; const r = payload1.then(res => { expect(res.value.data.testSubscription).to.equal('FIRST_EVENT'); @@ -93,10 +95,10 @@ describe('GraphQL-JS asyncIterator', () => { const origIterator = pubsub.asyncIterator(FIRST_EVENT); const schema = buildSchema(origIterator, () => Promise.resolve(true)); - const results = await subscribe({schema, document: query}) as AsyncIterator; + const results = await subscribe({ schema, document: query }) as AsyncIterableIterator; const payload1 = results.next(); - expect(isAsyncIterable(results)).to.be.true; + expect(isAsyncIterableIterator(results)).to.be.true; const r = payload1.then(res => { expect(res.value.data.testSubscription).to.equal('FIRST_EVENT'); @@ -133,8 +135,8 @@ describe('GraphQL-JS asyncIterator', () => { const schema = buildSchema(origIterator, filterFn); - subscribe({schema, document: query}).then((results: AsyncGenerator | ExecutionResult) => { - expect(isAsyncIterable(results)).to.be.true; + Promise.resolve(subscribe({ schema, document: query })).then((results: AsyncIterableIterator) => { + expect(isAsyncIterableIterator(results)).to.be.true; (results as AsyncGenerator).next(); (results as AsyncGenerator).return(); @@ -172,6 +174,19 @@ describe('GraphQL-JS asyncIterator', () => { }); }); +function isEven(x: number) { + if (x === undefined) { + throw Error('Undefined value passed to filterFn'); + } + return x % 2 === 0; +} + +const testFiniteAsyncIterator: AsyncIterableIterator = (async function * () { + for (const value of [1, 2, 3, 4, 5, 6, 7, 8]) { + yield value; + } +})(); + describe('withFilter', () => { it('works properly with finite asyncIterators', async () => { diff --git a/src/test/tests.ts b/src/test/tests.ts index 5190b31..23eab4f 100644 --- a/src/test/tests.ts +++ b/src/test/tests.ts @@ -7,7 +7,10 @@ import * as chaiAsPromised from 'chai-as-promised'; import * as sinonChai from 'sinon-chai'; import { PubSub } from '../pubsub'; -import { isAsyncIterable } from 'iterall'; + +const isAsyncIterableIterator = (input: unknown): input is AsyncIterableIterator => { + return input != null && typeof input[Symbol.asyncIterator] === 'function'; +}; chai.use(chaiAsPromised); chai.use(sinonChai); @@ -39,7 +42,7 @@ describe('AsyncIterator', () => { const ps = new PubSub(); const iterator = ps.asyncIterator(eventName); expect(iterator).to.not.be.undefined; - expect(isAsyncIterable(iterator)).to.be.true; + expect(isAsyncIterableIterator(iterator)).to.be.true; }); it('should trigger event on asyncIterator when published', done => { diff --git a/src/with-filter.ts b/src/with-filter.ts index 2403a73..f5c93e5 100644 --- a/src/with-filter.ts +++ b/src/with-filter.ts @@ -1,10 +1,9 @@ -import { $$asyncIterator } from 'iterall'; export type FilterFn = (rootValue?: TSource, args?: TArgs, context?: TContext, info?: any) => boolean | Promise; export type ResolverFn = (rootValue?: TSource, args?: TArgs, context?: TContext, info?: any) => AsyncIterator; -interface IterallAsyncIterator extends AsyncIterator { - [$$asyncIterator](): IterallAsyncIterator; +interface IterallAsyncIterator extends AsyncIterableIterator { + [Symbol.asyncIterator](): IterallAsyncIterator; } export type WithFilter = ( @@ -63,7 +62,7 @@ export function withFilter( throw(error) { return asyncIterator.throw(error); }, - [$$asyncIterator]() { + [Symbol.asyncIterator]() { return this; }, }; From 1d39b332a6716b09b69a4bd2373d7a41a0d78c7e Mon Sep 17 00:00:00 2001 From: n1ru4l Date: Mon, 21 Sep 2020 22:53:44 +0200 Subject: [PATCH 3/5] rename asyncIterator method to asyncIterableIterator. --- src/pubsub-engine.ts | 2 +- src/test/asyncIteratorSubscription.ts | 8 ++++---- src/test/tests.ts | 12 ++++++------ 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/pubsub-engine.ts b/src/pubsub-engine.ts index a6478da..1b0575e 100644 --- a/src/pubsub-engine.ts +++ b/src/pubsub-engine.ts @@ -4,7 +4,7 @@ export abstract class PubSubEngine { public abstract publish(triggerName: string, payload: any): Promise; public abstract subscribe(triggerName: string, onMessage: Function, options: Object): Promise; public abstract unsubscribe(subId: number); - public asyncIterator(triggers: string | string[]): PubSubAsyncIterableIterator { + public asyncIterableIterator(triggers: string | string[]): PubSubAsyncIterableIterator { return new PubSubAsyncIterableIterator(this, triggers); } } diff --git a/src/test/asyncIteratorSubscription.ts b/src/test/asyncIteratorSubscription.ts index a856ae8..6775527 100644 --- a/src/test/asyncIteratorSubscription.ts +++ b/src/test/asyncIteratorSubscription.ts @@ -67,7 +67,7 @@ describe('GraphQL-JS asyncIterator', () => { } `); const pubsub = new PubSub(); - const origIterator = pubsub.asyncIterator(FIRST_EVENT); + const origIterator = pubsub.asyncIterableIterator(FIRST_EVENT); const schema = buildSchema(origIterator); const results = await subscribe({ schema, document: query }) as AsyncIterableIterator; @@ -92,7 +92,7 @@ describe('GraphQL-JS asyncIterator', () => { } `); const pubsub = new PubSub(); - const origIterator = pubsub.asyncIterator(FIRST_EVENT); + const origIterator = pubsub.asyncIterableIterator(FIRST_EVENT); const schema = buildSchema(origIterator, () => Promise.resolve(true)); const results = await subscribe({ schema, document: query }) as AsyncIterableIterator; @@ -117,7 +117,7 @@ describe('GraphQL-JS asyncIterator', () => { `); const pubsub = new PubSub(); - const origIterator = pubsub.asyncIterator(FIRST_EVENT); + const origIterator = pubsub.asyncIterableIterator(FIRST_EVENT); let counter = 0; @@ -157,7 +157,7 @@ describe('GraphQL-JS asyncIterator', () => { `); const pubsub = new PubSub(); - const origIterator = pubsub.asyncIterator(FIRST_EVENT); + const origIterator = pubsub.asyncIterableIterator(FIRST_EVENT); const returnSpy = spy(origIterator, 'return'); const schema = buildSchema(origIterator); diff --git a/src/test/tests.ts b/src/test/tests.ts index 23eab4f..a84d605 100644 --- a/src/test/tests.ts +++ b/src/test/tests.ts @@ -40,7 +40,7 @@ describe('AsyncIterator', () => { it('should expose valid asyncIterator for a specific event', () => { const eventName = 'test'; const ps = new PubSub(); - const iterator = ps.asyncIterator(eventName); + const iterator = ps.asyncIterableIterator(eventName); expect(iterator).to.not.be.undefined; expect(isAsyncIterableIterator(iterator)).to.be.true; }); @@ -48,7 +48,7 @@ describe('AsyncIterator', () => { it('should trigger event on asyncIterator when published', done => { const eventName = 'test'; const ps = new PubSub(); - const iterator = ps.asyncIterator(eventName); + const iterator = ps.asyncIterableIterator(eventName); iterator.next().then(result => { expect(result).to.not.be.undefined; @@ -63,7 +63,7 @@ describe('AsyncIterator', () => { it('should not trigger event on asyncIterator when publishing other event', () => { const eventName = 'test2'; const ps = new PubSub(); - const iterator = ps.asyncIterator('test'); + const iterator = ps.asyncIterableIterator('test'); const spy = sinon.spy(); iterator.next().then(spy); @@ -74,7 +74,7 @@ describe('AsyncIterator', () => { it('register to multiple events', done => { const eventName = 'test2'; const ps = new PubSub(); - const iterator = ps.asyncIterator(['test', 'test2']); + const iterator = ps.asyncIterableIterator(['test', 'test2']); const spy = sinon.spy(); iterator.next().then(() => { @@ -88,7 +88,7 @@ describe('AsyncIterator', () => { it('should not trigger event on asyncIterator already returned', done => { const eventName = 'test'; const ps = new PubSub(); - const iterator = ps.asyncIterator(eventName); + const iterator = ps.asyncIterableIterator(eventName); iterator.next().then(result => { expect(result).to.deep.equal({ @@ -120,7 +120,7 @@ describe('AsyncIterator', () => { } } const ps = new TestPubSub(); - ps.asyncIterator(testEventName); + ps.asyncIterableIterator(testEventName); expect(ps.listenerCount(testEventName)).to.equal(0); }); From 37224fa4baeee4b6558090b8b7e7134c3a2ef5cd Mon Sep 17 00:00:00 2001 From: hwillson Date: Mon, 22 Nov 2021 20:38:31 -0500 Subject: [PATCH 4/5] Slight tweaks based on graphql-js 16 changes --- src/test/asyncIteratorSubscription.ts | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/test/asyncIteratorSubscription.ts b/src/test/asyncIteratorSubscription.ts index 6775527..d0f669d 100644 --- a/src/test/asyncIteratorSubscription.ts +++ b/src/test/asyncIteratorSubscription.ts @@ -135,7 +135,7 @@ describe('GraphQL-JS asyncIterator', () => { const schema = buildSchema(origIterator, filterFn); - Promise.resolve(subscribe({ schema, document: query })).then((results: AsyncIterableIterator) => { + Promise.resolve(subscribe({ schema, document: query })).then((results: AsyncIterableIterator | ExecutionResult) => { expect(isAsyncIterableIterator(results)).to.be.true; (results as AsyncGenerator).next(); @@ -188,19 +188,7 @@ const testFiniteAsyncIterator: AsyncIterableIterator = (async function * })(); describe('withFilter', () => { - it('works properly with finite asyncIterators', async () => { - const isEven = (x: number) => x % 2 === 0; - - const testFiniteAsyncIterator: AsyncIterator = createAsyncIterator([1, 2, 3, 4, 5, 6, 7, 8]); - // Work around https://github.com/leebyron/iterall/issues/48 - testFiniteAsyncIterator.throw = function (error) { - return Promise.reject(error); - }; - testFiniteAsyncIterator.return = function () { - return Promise.resolve({ value: undefined, done: true }); - }; - const filteredAsyncIterator = withFilter(() => testFiniteAsyncIterator, isEven)(); for (let i = 1; i <= 4; i++) { From cf130e199e7d3cbca520884921131cfdada4a4fe Mon Sep 17 00:00:00 2001 From: hwillson Date: Thu, 25 Nov 2021 12:51:09 -0500 Subject: [PATCH 5/5] Changelog update --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6c2ee11..bbca927 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ - Add an optional generic type map to `PubSub`.
[@cursorsdottsx](https://github.com/cursorsdottsx) in [#245](https://github.com/apollographql/graphql-subscriptions/pull/245) +- Replace `iterall` use with native `Symbol.asyncIterator`.
+ [@n1ru4l](https://github.com/n1ru4l) in [#232](https://github.com/apollographql/graphql-subscriptions/pull/232) ### 2.0.1 (not yet released)