Skip to content

Commit

Permalink
feat(Angular2Apollo): support for Rx.Observable
Browse files Browse the repository at this point in the history
  • Loading branch information
Kamil Kisiela committed Jul 20, 2016
1 parent 09f7227 commit d38cbcd
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 29 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Expand Up @@ -2,6 +2,8 @@

### vNEXT

- Added `ApolloQueryObservable` to support `Rx.Observable` in `Angular2Apollo.watchQuery` method ([PR #51](https://github.com/apollostack/angular2-apollo/pull/51))

### v0.4.0

- Passing all the options of mutation in `Apollo` decorator [PR #39](https://github.com/apollostack/angular2-apollo/pull/39)
Expand Down
2 changes: 2 additions & 0 deletions examples/hello-world/tsconfig.json
@@ -1,4 +1,6 @@
{
"compileOnSave": false,
"buildOnSave": false,
"compilerOptions": {
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
Expand Down
3 changes: 2 additions & 1 deletion package.json
Expand Up @@ -31,7 +31,8 @@
},
"peerDependencies": {
"apollo-client": "^0.3.0 || ^0.4.0",
"@angular/core": "^2.0.0-rc.1"
"@angular/core": "^2.0.0-rc.1",
"rxjs": "^5.0.0-beta.6"
},
"dependencies": {
"lodash.assign": "^4.0.9",
Expand Down
8 changes: 6 additions & 2 deletions src/angular2Apollo.ts
Expand Up @@ -8,6 +8,10 @@ import {
Inject,
} from '@angular/core';

import {
ApolloQueryObservable,
} from './apolloQueryObservable';

export const angularApolloClient = new OpaqueToken('AngularApolloClient');
export const defaultApolloClient = (client: ApolloClient): Provider => {
return provide(angularApolloClient, {
Expand All @@ -23,8 +27,8 @@ export class Angular2Apollo {

}

public watchQuery(options) {
return this.client.watchQuery(options);
public watchQuery(options): ApolloQueryObservable<any> {
return new ApolloQueryObservable(this.client.watchQuery(options));
}

public mutate(options) {
Expand Down
42 changes: 42 additions & 0 deletions src/apolloQueryObservable.ts
@@ -0,0 +1,42 @@
import {
Observable,
} from 'rxjs/Observable';

import {
Operator,
} from 'rxjs/Operator';

import {
Subscriber,
} from 'rxjs/Subscriber';

import {
Subscription,
} from 'rxjs/Subscription';

export class ApolloQueryObservable<T> extends Observable<T> {
constructor(public apollo: any, subscribe?: <R>(subscriber: Subscriber<R>) => Subscription | Function | void) {
super(subscribe);
}

public lift<T, R>(operator: Operator<T, R>): Observable<R> {
const observable = new ApolloQueryObservable<R>(this.apollo);

observable.source = this;
observable.operator = operator;

return observable;
}

public refetch(variables?: any): Promise<any> {
return this.apollo.refetch(variables);
}

public stopPolling(): void {
return this.apollo.stopPolling();
}

public startPolling(p: number): void {
return this.apollo.startPolling(p);
}
}
90 changes: 64 additions & 26 deletions tests/angular2Apollo.ts
Expand Up @@ -2,7 +2,9 @@ import {
Provider, ReflectiveInjector,
} from '@angular/core';

import ApolloClient from 'apollo-client';
import {
mockClient,
} from './_mocks';

import {
APOLLO_PROVIDERS,
Expand All @@ -14,41 +16,77 @@ import {
angularApolloClient,
} from '../src/angular2Apollo';

describe('angular2Apollo', () => {
const client = new ApolloClient();
import {
ApolloQueryObservable,
} from '../src/apolloQueryObservable';

describe('Angular2Apollo', () => {
/**
* Gets Angular2Apollo service and calls a method
*
* Checks if method with the same name has been called
* with the same same options
*
* It also checks if service method returns result of ApolloClient method
*
* @param {string} method Name of method you want to test
* @param {any} options Used options
* @param {any} result Mock result
*/
function rawApiCall(method: string, options = 'options', result = 'result') {
spyOn(client, method).and.returnValue(result);
import ApolloClient from 'apollo-client';

const injector = ReflectiveInjector.resolveAndCreate([defaultApolloClient(client), APOLLO_PROVIDERS]);
const service = injector.get(Angular2Apollo);
import gql from 'graphql-tag';

expect(service[method](options)).toBe(result);
expect(client[method]).toHaveBeenCalledWith(options);
const query = gql`
query heroes {
allHeroes {
heroes {
name
}
}
}
`;

const data = {
allHeroes: {
heroes: [{ name: 'Mr Foo' }, { name: 'Mr Bar' }],
},
};

const client = mockClient({
request: { query },
result: { data },
});

describe('angular2Apollo', () => {
describe('Angular2Apollo', () => {
let angular2Apollo;

beforeEach(() => {
const injector = ReflectiveInjector.resolveAndCreate([defaultApolloClient(client), APOLLO_PROVIDERS]);
angular2Apollo = injector.get(Angular2Apollo);
});

describe('watchQuery()', () => {
it('should call same method on client with same args and return it', () => {
rawApiCall('watchQuery');
it('should be called with the same options', () => {
const options = { query };

spyOn(client, 'watchQuery').and.callThrough();

angular2Apollo.watchQuery(options);

expect(client.watchQuery).toHaveBeenCalledWith(options);
});

describe('result', () => {
let obs;

beforeEach(() => {
obs = angular2Apollo.watchQuery({ query });
});

it('should return the ApolloQueryObserable', () => {
expect(obs instanceof ApolloQueryObservable).toEqual(true);
});
});
});

describe('mutate()', () => {
it('should call same method on client with same args and return it', () => {
rawApiCall('mutate');
it('should be called with the same options', () => {
const options = {mutation: '', variables: {}};

spyOn(client, 'mutate').and.returnValue('return');

angular2Apollo.mutate(options);

expect(client.mutate).toHaveBeenCalledWith(options);
});
});
});
Expand Down
49 changes: 49 additions & 0 deletions tests/apolloQueryObservable.ts
@@ -0,0 +1,49 @@
import {
Observable,
} from 'rxjs/Observable';

import {
ApolloQueryObservable,
} from '../src/apolloQueryObservable';

import 'rxjs/add/operator/map';

class ObservableQuery<T> extends Observable<T> {
refetch(v?: any) {}
stopPolling() {}
startPolling(n?: any) {}
}

describe('ApolloQueryObservable', () => {
it('should be able to call refetch()', () => {
const obs = new ObservableQuery();
const res = new ApolloQueryObservable(obs);
const variables = { foo: true };

spyOn(obs, 'refetch').and.returnValue('refetch');

expect(res.refetch(variables)).toEqual('refetch');
expect(obs.refetch).toHaveBeenCalledWith(variables);
});

it('should be able to call stopPolling()', () => {
const obs = new ObservableQuery();
const res = new ApolloQueryObservable(obs);

spyOn(obs, 'stopPolling').and.returnValue('stopPolling');

expect(res.stopPolling()).toEqual('stopPolling');
expect(obs.stopPolling).toHaveBeenCalled();
});

it('should be able to call custom method after operator', () => {
const obs = new ObservableQuery();
spyOn(obs, 'refetch').and.returnValue('refetch');

const res = new ApolloQueryObservable(obs).map((i) => i);


expect(res['refetch']()).toEqual('refetch');
expect(obs.refetch).toHaveBeenCalled();
});
});
1 change: 1 addition & 0 deletions tests/index.ts
Expand Up @@ -4,5 +4,6 @@ import 'reflect-metadata';

// tests
import './angular2Apollo';
import './apolloQueryObservable';
import './apolloQueryPipe';
import './apolloDecorator/index';

0 comments on commit d38cbcd

Please sign in to comment.