Skip to content

Commit

Permalink
Fixed single response and added subscriptions to links (apollographql…
Browse files Browse the repository at this point in the history
…#1992)

* Fixed single response and added subscriptions to links

* remove Observable Network Interface and include subscriptions map for current interface

* Subscripitons unit tests and subscribe method calls correctly to handler

* Update Changelog to reflect subscriptions with Links

* bumped graphql types version and include asynciterable ts library
  • Loading branch information
evans authored and James Baxley committed Aug 9, 2017
1 parent b9e0184 commit 2433f1d
Show file tree
Hide file tree
Showing 6 changed files with 123 additions and 14 deletions.
1 change: 1 addition & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
{
"name": "Test",
"type": "node",
"request": "launch",
"program": "${workspaceRoot}/node_modules/mocha/bin/_mocha",
"stopOnEntry": false,
"args": [
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### vNEXT

-Add support for subscriptions with Apollo Link network stack [PR #1992](https://github.com/apollographql/apollo-client/pull/1992)
-fixed `resolved` scoping issue for multiple queries in flight with Apollo Link [PR #2002](https://github.com/apollographql/apollo-client/pull/2002)

### 1.9.0
Expand Down
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,6 @@
"webpack-bundle-analyzer": "2.9.0"
},
"optionalDependencies": {
"@types/async": "2.0.40",
"@types/graphql": "0.9.4"
"@types/graphql": "0.10.2"
}
}
50 changes: 38 additions & 12 deletions src/ApolloClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import {
Request,
} from './transport/networkInterface';

import { execute, ApolloLink } from 'apollo-link-core';
import {
execute,
ApolloLink,
FetchResult,
Observable as ZenObservable,
} from 'apollo-link-core';
import { assign } from './util/assign';

import {
Expand Down Expand Up @@ -131,6 +136,7 @@ export default class ApolloClient implements DataProxy {
private proxy: DataProxy | undefined;
private fragmentMatcher: FragmentMatcherInterface;
private ssrMode: boolean;
private subscriptionMap: Map<string, ZenObservable.Subscription>;

/**
* Constructs an instance of {@link ApolloClient}.
Expand Down Expand Up @@ -229,25 +235,45 @@ export default class ApolloClient implements DataProxy {
};

if (networkInterface instanceof ApolloLink) {
let count = 0;
this.networkInterface = {
query: createQuery((request: Request) => {
return (execute(
networkInterface as ApolloLink,
request,
) as any) as Observable<ExecutionResult>;
}),
subscribe: (request: any, handler: any): string => {
if (!this.subscriptionMap) {
this.subscriptionMap = new Map<
string,
ZenObservable.Subscription
>();
}

const subscription = (execute(
networkInterface as ApolloLink,
request,
) as any).subscribe({
next: (data: FetchResult) => handler(undefined, data),
error: (error: Error) => handler([error]),
complete: handler,
});

const id = count.toString();
this.subscriptionMap.set(id, subscription);
count++;
return id;
},
unsubscribe: (id: string): void => {
if (this.subscriptionMap) {
const subscription = this.subscriptionMap.get(id);
if (subscription) {
subscription.unsubscribe();
}
}
},
};
} else if (
networkInterface &&
typeof (<ObservableNetworkInterface>networkInterface).request ===
'function'
) {
console.warn(`The Observable Network interface will be deprecated`);
this.networkInterface = assign(networkInterface || {}, {
query: createQuery(
(networkInterface as ObservableNetworkInterface).request,
),
});
} else {
this.networkInterface = networkInterface
? <NetworkInterface>networkInterface
Expand Down
81 changes: 81 additions & 0 deletions test/graphqlSubscriptions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ import { QueryManager } from '../src/core/QueryManager';

import { createApolloStore } from '../src/store';

import { SubscriptionOptions } from '../src/core/watchQueryOptions';

import { ApolloLink, Observable, FetchResult } from 'apollo-link-core';

describe('GraphQL Subscriptions', () => {
const results = [
'Dahivat Pandya',
Expand Down Expand Up @@ -205,6 +209,83 @@ describe('GraphQL Subscriptions', () => {
}
});

it('should receive multiple results for a subscription with Apollo Link', done => {
let numResults = 0;
const expectedData = [
'Dahivat Pandya',
'Vyacheslav Kim',
'Changping Chen',
'Amanda Liu',
].map(name => ({ user: { name: name } }));
const queryInfo = {
request: defaultSub1.request || ({} as any),
results: [...expectedData],
};

const link = ApolloLink.from([
operation =>
new Observable(observer => {
if (queryInfo.results) {
queryInfo.results.map(result =>
observer.next(result as FetchResult),
);
}
}),
]);
const client = new ApolloClient({
networkInterface: link,
addTypename: false,
});

const obs = client.subscribe(queryInfo.request as SubscriptionOptions);
const sub = obs.subscribe({
next: data => {
const expected = expectedData.shift();
if (expected) {
assert.equal(data, expected);
} else {
assert(false);
}
if (expectedData.length === 0) {
done();
}
},
error: console.log,
complete: () => assert(false),
});
});

it('should unsubscribe properly with Apollo Link', () => {
let numResults = 0;
const readyToUnsub: boolean[] = [];

const link = ApolloLink.from([
operation =>
new Observable(observer => {
const index = readyToUnsub.length;
readyToUnsub.push(false);
return () => {
assert(readyToUnsub[index]);
};
}),
]);
const client = new ApolloClient({
networkInterface: link,
addTypename: false,
});

const obs0 = client.subscribe(defaultSub1.request as SubscriptionOptions);
const obs1 = client.subscribe(defaultSub1.request as SubscriptionOptions);
const subscription0 = obs0.subscribe({});
const subscription1 = obs1.subscribe({});

readyToUnsub[1] = true;
subscription1.unsubscribe();
readyToUnsub[0] = true;
subscription0.unsubscribe();
readyToUnsub.map(bool => assert(bool));
});

it('should fire redux action and call result reducers', done => {
const query = gql`
query miniQuery {
Expand Down
1 change: 1 addition & 0 deletions tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
},
"files": [
"node_modules/typescript/lib/lib.es2015.d.ts",
"node_modules/typescript/lib/lib.esnext.asynciterable.d.ts",
"node_modules/typescript/lib/lib.dom.d.ts",
"typings.d.ts",
"fetch-mock.typings.d.ts",
Expand Down

0 comments on commit 2433f1d

Please sign in to comment.