diff --git a/.changeset/thin-otters-move.md b/.changeset/thin-otters-move.md new file mode 100644 index 000000000..ce0d34ee2 --- /dev/null +++ b/.changeset/thin-otters-move.md @@ -0,0 +1,5 @@ +--- +'apollo-angular': patch +--- + +Let types flow naturally from `ObservableQuery` diff --git a/packages/apollo-angular/src/query-ref.ts b/packages/apollo-angular/src/query-ref.ts index e21aaaafd..5a1462499 100644 --- a/packages/apollo-angular/src/query-ref.ts +++ b/packages/apollo-angular/src/query-ref.ts @@ -4,7 +4,6 @@ import type { ApolloQueryResult, ObservableQuery, OperationVariables, - SubscribeToMoreOptions, TypedDocumentNode, } from '@apollo/client/core'; import { NetworkStatus } from '@apollo/client/core'; @@ -43,99 +42,79 @@ function useInitialLoading(obsQuery: Observable export type QueryRefFromDocument = T extends TypedDocumentNode ? QueryRef : never; -export class QueryRef { +export class QueryRef + implements + Pick< + ObservableQuery, + | 'queryId' + | 'options' + | 'variables' + | 'result' + | 'getCurrentResult' + | 'getLastResult' + | 'getLastError' + | 'resetLastResults' + | 'refetch' + | 'fetchMore' + | 'subscribeToMore' + | 'updateQuery' + | 'stopPolling' + | 'startPolling' + | 'setOptions' + | 'setVariables' + > +{ public readonly valueChanges: Observable>; - public readonly queryId: ObservableQuery['queryId']; + + // Types flow straight from ObservableQuery + public readonly queryId; + public readonly result; + public readonly getCurrentResult; + public readonly getLastResult; + public readonly getLastError; + public readonly resetLastResults; + public readonly refetch; + public readonly fetchMore; + public readonly subscribeToMore; + public readonly updateQuery; + public readonly stopPolling; + public readonly startPolling; + public readonly setOptions; + public readonly setVariables; constructor( - private readonly obsQuery: ObservableQuery, + private readonly query: ObservableQuery, ngZone: NgZone, options: WatchQueryOptions, ) { - const wrapped = wrapWithZone(from(fixObservable(this.obsQuery)), ngZone); + const wrapped = wrapWithZone(from(fixObservable(this.query)), ngZone); this.valueChanges = options.useInitialLoading - ? wrapped.pipe(useInitialLoading(this.obsQuery)) + ? wrapped.pipe(useInitialLoading(this.query)) : wrapped; - this.queryId = this.obsQuery.queryId; - } - - // ObservableQuery's methods - - public get options(): ObservableQuery['options'] { - return this.obsQuery.options; - } - - public get variables(): ObservableQuery['variables'] { - return this.obsQuery.variables; - } - - public result(): ReturnType['result']> { - return this.obsQuery.result(); - } - - public getCurrentResult(): ReturnType['getCurrentResult']> { - return this.obsQuery.getCurrentResult(); - } - - public getLastResult(): ReturnType['getLastResult']> { - return this.obsQuery.getLastResult(); - } - - public getLastError(): ReturnType['getLastError']> { - return this.obsQuery.getLastError(); - } - - public resetLastResults(): ReturnType['resetLastResults']> { - return this.obsQuery.resetLastResults(); - } - - public refetch( - variables?: Parameters['refetch']>[0], - ): ReturnType['refetch']> { - return this.obsQuery.refetch(variables); - } - - public fetchMore( - fetchMoreOptions: Parameters['fetchMore']>[0], - ): ReturnType['fetchMore']> { - return this.obsQuery.fetchMore(fetchMoreOptions); - } - - public subscribeToMore< - TSubscriptionData = TData, - TSubscriptionVariables extends OperationVariables = TVariables, - >( - options: SubscribeToMoreOptions, - ): ReturnType['subscribeToMore']> { - return this.obsQuery.subscribeToMore(options); - } - - public updateQuery( - mapFn: Parameters['updateQuery']>[0], - ): ReturnType['updateQuery']> { - return this.obsQuery.updateQuery(mapFn); - } - - public stopPolling(): ReturnType['stopPolling']> { - return this.obsQuery.stopPolling(); - } - - public startPolling( - pollInterval: Parameters['startPolling']>[0], - ): ReturnType['startPolling']> { - return this.obsQuery.startPolling(pollInterval); - } - - public setOptions( - opts: Parameters['setOptions']>[0], - ): ReturnType['setOptions']> { - return this.obsQuery.setOptions(opts); - } - - public setVariables( - variables: Parameters['setVariables']>[0], - ): ReturnType['setVariables']> { - return this.obsQuery.setVariables(variables); + this.queryId = this.query.queryId; + + // ObservableQuery's methods + this.result = this.query.result.bind(this.query); + this.getCurrentResult = this.query.getCurrentResult.bind(this.query); + this.getLastResult = this.query.getLastResult.bind(this.query); + this.getLastError = this.query.getLastError.bind(this.query); + this.resetLastResults = this.query.resetLastResults.bind(this.query); + this.refetch = this.query.refetch.bind(this.query); + this.fetchMore = this.query.fetchMore.bind(this.query); + this.subscribeToMore = this.query.subscribeToMore.bind(this.query); + this.updateQuery = this.query.updateQuery.bind(this.query); + this.stopPolling = this.query.stopPolling.bind(this.query); + this.startPolling = this.query.startPolling.bind(this.query); + this.setOptions = this.query.setOptions.bind(this.query); + this.setVariables = this.query.setVariables.bind(this.query); + } + + public get options() { + return this.query.options; + } + + public get variables() { + return this.query.variables; } } diff --git a/packages/apollo-angular/tests/Apollo.spec.ts b/packages/apollo-angular/tests/Apollo.spec.ts index a993d1a6b..fb1789adf 100644 --- a/packages/apollo-angular/tests/Apollo.spec.ts +++ b/packages/apollo-angular/tests/Apollo.spec.ts @@ -83,10 +83,10 @@ describe('Apollo', () => { `, }; - client.watchQuery = jest.fn().mockReturnValue(new Observable()); + const spy = jest.spyOn(client, 'watchQuery'); apollo.watchQuery(options); - expect(client.watchQuery).toBeCalledWith(options); + expect(spy).toBeCalledWith(options); }); test('should be able to refetch', (done: jest.DoneCallback) => { diff --git a/packages/apollo-angular/tests/QueryRef.spec.ts b/packages/apollo-angular/tests/QueryRef.spec.ts index 9fbff5755..1784df1b2 100644 --- a/packages/apollo-angular/tests/QueryRef.spec.ts +++ b/packages/apollo-angular/tests/QueryRef.spec.ts @@ -12,8 +12,16 @@ const createClient = (link: ApolloLink) => cache: new InMemoryCache(), }); +type Result = { + heroes: { name: string }[]; +}; + +type Variables = { + foo?: number; +}; + const heroesOperation = { - query: gql` + query: gql` query allHeroes { heroes { name @@ -40,8 +48,7 @@ const Batman = { describe('QueryRef', () => { let ngZone: NgZone; let client: ApolloClient; - let obsQuery: ObservableQuery; - let queryRef: QueryRef; + let obsQuery: ObservableQuery; beforeEach(() => { ngZone = { run: jest.fn(cb => cb()) } as any; @@ -58,10 +65,14 @@ describe('QueryRef', () => { client = createClient(mockedLink); obsQuery = client.watchQuery(heroesOperation); - queryRef = new QueryRef(obsQuery, ngZone, {} as any); }); + function createQueryRef(obsQuery: ObservableQuery): QueryRef { + return new QueryRef(obsQuery, ngZone, { query: heroesOperation.query }); + } + test('should listen to changes', done => { + const queryRef = createQueryRef(obsQuery); queryRef.valueChanges.subscribe({ next: result => { expect(result.data).toBeDefined(); @@ -77,6 +88,7 @@ describe('QueryRef', () => { const mockCallback = jest.fn(); obsQuery.refetch = mockCallback; + const queryRef = createQueryRef(obsQuery); queryRef.refetch(); expect(mockCallback.mock.calls.length).toBe(1); @@ -85,6 +97,7 @@ describe('QueryRef', () => { test('should be able refetch and receive new results', done => { let calls = 0; + const queryRef = createQueryRef(obsQuery); queryRef.valueChanges.subscribe({ next: result => { calls++; @@ -110,6 +123,7 @@ describe('QueryRef', () => { test('should be able refetch and receive new results after using rxjs operator', done => { let calls = 0; + const queryRef = createQueryRef(obsQuery); const obs = queryRef.valueChanges; obs.pipe(map(result => result.data)).subscribe({ @@ -139,9 +153,10 @@ describe('QueryRef', () => { test('should be able to call updateQuery()', () => { const mockCallback = jest.fn(); - const mapFn = () => ({}); + const mapFn = () => undefined; obsQuery.updateQuery = mockCallback; + const queryRef = createQueryRef(obsQuery); queryRef.updateQuery(mapFn); expect(mockCallback.mock.calls.length).toBe(1); @@ -152,6 +167,7 @@ describe('QueryRef', () => { const mockCallback = jest.fn(); obsQuery.result = mockCallback.mockReturnValue('expected'); + const queryRef = createQueryRef(obsQuery); const result = queryRef.result(); expect(result).toBe('expected'); @@ -160,6 +176,7 @@ describe('QueryRef', () => { test('should be able to call getCurrentResult() and get updated results', done => { let calls = 0; + const queryRef = createQueryRef(obsQuery); const obs = queryRef.valueChanges; obs.pipe(map(result => result.data)).subscribe({ @@ -189,6 +206,7 @@ describe('QueryRef', () => { const mockCallback = jest.fn(); obsQuery.getLastResult = mockCallback.mockReturnValue('expected'); + const queryRef = createQueryRef(obsQuery); const result = queryRef.getLastResult(); expect(result).toBe('expected'); @@ -199,6 +217,7 @@ describe('QueryRef', () => { const mockCallback = jest.fn(); obsQuery.getLastError = mockCallback.mockReturnValue('expected'); + const queryRef = createQueryRef(obsQuery); const result = queryRef.getLastError(); expect(result).toBe('expected'); @@ -209,6 +228,7 @@ describe('QueryRef', () => { const mockCallback = jest.fn(); obsQuery.resetLastResults = mockCallback.mockReturnValue('expected'); + const queryRef = createQueryRef(obsQuery); const result = queryRef.resetLastResults(); expect(result).toBe('expected'); @@ -217,10 +237,11 @@ describe('QueryRef', () => { test('should be able to call fetchMore()', () => { const mockCallback = jest.fn(); - const opts = { foo: 1 }; + const opts = { variables: { foo: 1 } }; obsQuery.fetchMore = mockCallback.mockReturnValue('expected'); - const result = queryRef.fetchMore(opts as any); + const queryRef = createQueryRef(obsQuery); + const result = queryRef.fetchMore(opts); expect(result).toBe('expected'); expect(mockCallback.mock.calls.length).toBe(1); @@ -229,10 +250,11 @@ describe('QueryRef', () => { test('should be able to call subscribeToMore()', () => { const mockCallback = jest.fn(); - const opts = { foo: 1 }; + const opts = { document: heroesOperation.query }; obsQuery.subscribeToMore = mockCallback; - queryRef.subscribeToMore(opts as any); + const queryRef = createQueryRef(obsQuery); + queryRef.subscribeToMore(opts); expect(mockCallback.mock.calls.length).toBe(1); expect(mockCallback.mock.calls[0][0]).toBe(opts); @@ -242,6 +264,7 @@ describe('QueryRef', () => { const mockCallback = jest.fn(); obsQuery.stopPolling = mockCallback; + const queryRef = createQueryRef(obsQuery); queryRef.stopPolling(); expect(mockCallback.mock.calls.length).toBe(1); @@ -251,6 +274,7 @@ describe('QueryRef', () => { const mockCallback = jest.fn(); obsQuery.startPolling = mockCallback; + const queryRef = createQueryRef(obsQuery); queryRef.startPolling(3000); expect(mockCallback.mock.calls.length).toBe(1); @@ -262,6 +286,7 @@ describe('QueryRef', () => { const opts = {}; obsQuery.setOptions = mockCallback.mockReturnValue('expected'); + const queryRef = createQueryRef(obsQuery); const result = queryRef.setOptions(opts); expect(result).toBe('expected'); @@ -274,6 +299,7 @@ describe('QueryRef', () => { const variables = {}; obsQuery.setVariables = mockCallback.mockReturnValue('expected'); + const queryRef = createQueryRef(obsQuery); const result = queryRef.setVariables(variables); expect(result).toBe('expected'); @@ -282,6 +308,7 @@ describe('QueryRef', () => { }); test('should handle multiple subscribers', done => { + const queryRef = createQueryRef(obsQuery); const obsFirst = queryRef.valueChanges; const obsSecond = queryRef.valueChanges; @@ -338,6 +365,7 @@ describe('QueryRef', () => { }); test('should unsubscribe', done => { + const queryRef = createQueryRef(obsQuery); const obs = queryRef.valueChanges; const id = queryRef.queryId; @@ -356,6 +384,7 @@ describe('QueryRef', () => { test('should unsubscribe based on rxjs operators', done => { const gate = new Subject(); + const queryRef = createQueryRef(obsQuery); const obs = queryRef.valueChanges.pipe(takeUntil(gate)); const id = queryRef.queryId;