Skip to content

Commit

Permalink
feat: replace iterall with native Symbol.asyncIterator + fix return t…
Browse files Browse the repository at this point in the history
…ypes (#232)

* 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).

* remove iterall

* rename asyncIterator method to asyncIterableIterator.

* Slight tweaks based on graphql-js 16 changes

* Changelog update

Co-authored-by: hwillson <hugh@octonary.com>
  • Loading branch information
n1ru4l and hwillson committed Nov 25, 2021
1 parent 8e8d511 commit e27eb2e
Show file tree
Hide file tree
Showing 7 changed files with 49 additions and 46 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

- Add an optional generic type map to `PubSub`. <br/>
[@cursorsdottsx](https://github.com/cursorsdottsx) in [#245](https://github.com/apollographql/graphql-subscriptions/pull/245)
- Replace `iterall` use with native `Symbol.asyncIterator`. <br/>
[@n1ru4l](https://github.com/n1ru4l) in [#232](https://github.com/apollographql/graphql-subscriptions/pull/232)

### 2.0.1 (not yet released)

Expand Down
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { $$asyncIterator } from 'iterall';
import { PubSubEngine } from './pubsub-engine';

/**
Expand Down Expand Up @@ -33,7 +32,7 @@ import { PubSubEngine } from './pubsub-engine';
* @property pubsub @type {PubSubEngine}
* The PubSubEngine whose events will be observed.
*/
export class PubSubAsyncIterator<T> implements AsyncIterator<T> {
export class PubSubAsyncIterableIterator<T> implements AsyncIterableIterator<T> {

private pullQueue: ((value: IteratorResult<T>) => void)[];
private pushQueue: T[];
Expand Down Expand Up @@ -66,7 +65,7 @@ export class PubSubAsyncIterator<T> implements AsyncIterator<T> {
return Promise.reject(error);
}

public [$$asyncIterator]() {
public [Symbol.asyncIterator]() {
return this;
}

Expand Down Expand Up @@ -119,5 +118,4 @@ export class PubSubAsyncIterator<T> implements AsyncIterator<T> {
this.pubsub.unsubscribe(subscriptionId);
}
}

}
6 changes: 3 additions & 3 deletions src/pubsub-engine.ts
Original file line number Diff line number Diff line change
@@ -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<void>;
public abstract subscribe(triggerName: string, onMessage: Function, options: Object): Promise<number>;
public abstract unsubscribe(subId: number);
public asyncIterator<T>(triggers: string | string[]): AsyncIterator<T> {
return new PubSubAsyncIterator<T>(this, triggers);
public asyncIterableIterator<T>(triggers: string | string[]): PubSubAsyncIterableIterator<T> {
return new PubSubAsyncIterableIterator<T>(this, triggers);
}
}
51 changes: 27 additions & 24 deletions src/test/asyncIteratorSubscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<unknown> => {
return input != null && typeof input[Symbol.asyncIterator] === 'function';
};

chai.use(chaiAsPromised);
chai.use(sinonChai);
const expect = chai.expect;
Expand Down Expand Up @@ -64,14 +67,13 @@ 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 AsyncIterator<ExecutionResult>;
const results = await subscribe({ schema, document: query }) as AsyncIterableIterator<ExecutionResult>;
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');
Expand All @@ -90,13 +92,13 @@ 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 AsyncIterator<ExecutionResult>;
const results = await subscribe({ schema, document: query }) as AsyncIterableIterator<ExecutionResult>;
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');
Expand All @@ -115,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;

Expand All @@ -133,8 +135,8 @@ describe('GraphQL-JS asyncIterator', () => {

const schema = buildSchema(origIterator, filterFn);

subscribe({schema, document: query}).then((results: AsyncGenerator<ExecutionResult, void, void> | ExecutionResult) => {
expect(isAsyncIterable(results)).to.be.true;
Promise.resolve(subscribe({ schema, document: query })).then((results: AsyncIterableIterator<ExecutionResult> | ExecutionResult) => {
expect(isAsyncIterableIterator(results)).to.be.true;

(results as AsyncGenerator<ExecutionResult, void, void>).next();
(results as AsyncGenerator<ExecutionResult, void, void>).return();
Expand All @@ -155,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);

Expand All @@ -172,20 +174,21 @@ describe('GraphQL-JS asyncIterator', () => {
});
});

describe('withFilter', () => {

it('works properly with finite asyncIterators', async () => {
const isEven = (x: number) => x % 2 === 0;
function isEven(x: number) {
if (x === undefined) {
throw Error('Undefined value passed to filterFn');
}
return x % 2 === 0;
}

const testFiniteAsyncIterator: AsyncIterator<number> = 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 testFiniteAsyncIterator: AsyncIterableIterator<number> = (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 () => {
const filteredAsyncIterator = withFilter(() => testFiniteAsyncIterator, isEven)();

for (let i = 1; i <= 4; i++) {
Expand Down
19 changes: 11 additions & 8 deletions src/test/tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<unknown> => {
return input != null && typeof input[Symbol.asyncIterator] === 'function';
};

chai.use(chaiAsPromised);
chai.use(sinonChai);
Expand Down Expand Up @@ -37,15 +40,15 @@ 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(isAsyncIterable(iterator)).to.be.true;
expect(isAsyncIterableIterator(iterator)).to.be.true;
});

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;
Expand All @@ -60,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);
Expand All @@ -71,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(() => {
Expand All @@ -85,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({
Expand Down Expand Up @@ -117,7 +120,7 @@ describe('AsyncIterator', () => {
}
}
const ps = new TestPubSub();
ps.asyncIterator(testEventName);
ps.asyncIterableIterator(testEventName);

expect(ps.listenerCount(testEventName)).to.equal(0);
});
Expand Down
7 changes: 3 additions & 4 deletions src/with-filter.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { $$asyncIterator } from 'iterall';

export type FilterFn<TSource = any, TArgs = any, TContext = any> = (rootValue?: TSource, args?: TArgs, context?: TContext, info?: any) => boolean | Promise<boolean>;
export type ResolverFn<TSource = any, TArgs = any, TContext = any> = (rootValue?: TSource, args?: TArgs, context?: TContext, info?: any) => AsyncIterator<any>;

interface IterallAsyncIterator<T> extends AsyncIterator<T> {
[$$asyncIterator](): IterallAsyncIterator<T>;
interface IterallAsyncIterator<T> extends AsyncIterableIterator<T> {
[Symbol.asyncIterator](): IterallAsyncIterator<T>;
}

export type WithFilter<TSource = any, TArgs = any, TContext = any> = (
Expand Down Expand Up @@ -63,7 +62,7 @@ export function withFilter<TSource = any, TArgs = any, TContext = any>(
throw(error) {
return asyncIterator.throw(error);
},
[$$asyncIterator]() {
[Symbol.asyncIterator]() {
return this;
},
};
Expand Down

0 comments on commit e27eb2e

Please sign in to comment.