diff --git a/packages/apollo-angular/CHANGELOG.md b/packages/apollo-angular/CHANGELOG.md index 5267c24ec..2b663eadd 100644 --- a/packages/apollo-angular/CHANGELOG.md +++ b/packages/apollo-angular/CHANGELOG.md @@ -1,5 +1,26 @@ # Change log +## 12.1.0 + +### Minor Changes + +- [#2382](https://github.com/the-guild-org/apollo-angular/pull/2382) + [`d21e7f9`](https://github.com/the-guild-org/apollo-angular/commit/d21e7f90de50d0b7b2afc3692b8b569803209ea5) + Thanks [@PowerKiKi](https://github.com/PowerKiKi)! - New `onlyCompleteFragment()` + + Same as `onlyCompleteData()` but for `Apollo.watchFragment()`. + +### Patch Changes + +- [#2381](https://github.com/the-guild-org/apollo-angular/pull/2381) + [`679dba2`](https://github.com/the-guild-org/apollo-angular/commit/679dba2ca47d859ed2be5441f12eca3ca72ceea1) + Thanks [@PowerKiKi](https://github.com/PowerKiKi)! - Rename `onlyComplete()` into + `onlyCompleteData()` + + Because it communicates better that it is about the data, and not the stream being completed. + + `onlyComplete()` will be dropped in the next major version. + ## 12.0.0 ### Major Changes diff --git a/packages/apollo-angular/package.json b/packages/apollo-angular/package.json index 365614cc7..806e04599 100644 --- a/packages/apollo-angular/package.json +++ b/packages/apollo-angular/package.json @@ -1,6 +1,6 @@ { "name": "apollo-angular", - "version": "12.0.0", + "version": "12.1.0", "type": "module", "description": "Use your GraphQL data in your Angular app, with the Apollo Client", "repository": { diff --git a/packages/apollo-angular/src/index.ts b/packages/apollo-angular/src/index.ts index 2cf00e6f6..2e09ff8dc 100644 --- a/packages/apollo-angular/src/index.ts +++ b/packages/apollo-angular/src/index.ts @@ -8,4 +8,4 @@ export { Subscription } from './subscription'; export { APOLLO_OPTIONS, APOLLO_NAMED_OPTIONS, APOLLO_FLAGS } from './tokens'; export type { Flags, NamedOptions, ResultOf, VariablesOf } from './types'; export { gql } from './gql'; -export { onlyComplete } from './only-complete'; +export { onlyCompleteData, onlyComplete, onlyCompleteFragment } from './only-complete-data'; diff --git a/packages/apollo-angular/src/only-complete.ts b/packages/apollo-angular/src/only-complete-data.ts similarity index 59% rename from packages/apollo-angular/src/only-complete.ts rename to packages/apollo-angular/src/only-complete-data.ts index b3bc8cbd5..4d5bd3b93 100644 --- a/packages/apollo-angular/src/only-complete.ts +++ b/packages/apollo-angular/src/only-complete-data.ts @@ -1,5 +1,15 @@ import { filter, type OperatorFunction } from 'rxjs'; -import type { ObservableQuery } from '@apollo/client/core'; +import type { ApolloClient, GetDataState, ObservableQuery } from '@apollo/client/core'; + +type CompleteFragment = { + complete: true; + missing?: never; +} & GetDataState; + +type ForWatchFragment = OperatorFunction< + ApolloClient.WatchFragmentResult, + CompleteFragment +>; /** * Filter emitted results to only receive results that are complete (`result.dataState === 'complete'`). @@ -19,13 +29,13 @@ import type { ObservableQuery } from '@apollo/client/core'; * notifyOnNetworkStatusChange: false, // Adding this will save CPU cycles * }) * .valueChanges - * .pipe(onlyComplete()) + * .pipe(onlyCompleteData()) * .subscribe(result => { * // Do something with complete result * }); * ``` */ -export function onlyComplete(): OperatorFunction< +export function onlyCompleteData(): OperatorFunction< ObservableQuery.Result, ObservableQuery.Result > { @@ -34,3 +44,15 @@ export function onlyComplete(): OperatorFunction< result.dataState === 'complete', ); } + +/** + * @deprecated Use `onlyCompleteData()` instead. + */ +export const onlyComplete = onlyCompleteData; + +/** + * Same as `onlyCompleteData()` but for `Apollo.watchFragment()`. + */ +export function onlyCompleteFragment(): ForWatchFragment { + return filter((result): result is CompleteFragment => result.dataState === 'complete'); +} diff --git a/packages/apollo-angular/testing/tests/only-complete-data.spec.ts b/packages/apollo-angular/testing/tests/only-complete-data.spec.ts new file mode 100644 index 000000000..98651fe6a --- /dev/null +++ b/packages/apollo-angular/testing/tests/only-complete-data.spec.ts @@ -0,0 +1,112 @@ +import { Apollo, gql, onlyCompleteData, onlyCompleteFragment, provideApollo } from 'apollo-angular'; +import { map, Subject } from 'rxjs'; +import { describe, expect, test } from 'vitest'; +import { TestBed } from '@angular/core/testing'; +import { InMemoryCache, NetworkStatus, type ObservableQuery } from '@apollo/client/core'; +import { MockLink } from '@apollo/client/testing'; + +interface Result { + user: { + name: string; + }; +} + +const query = gql>` + query User { + user { + name + } + } +`; + +const fragment = gql>` + fragment UserFragment on User { + user { + name + } + } +`; + +describe('onlyCompleteData', () => { + let theUser: Result['user'] | null = null; + let count = 0; + + test('should receive only complete results', () => + new Promise(done => { + const b = new Subject>(); + b.pipe(onlyCompleteData()).subscribe({ + next: result => { + count++; + theUser = result.data.user; + }, + complete: () => { + expect(count).toBe(1); + expect(theUser).toEqual({ name: 'foo' }); + done(); + }, + }); + + b.next({ + dataState: 'partial', + data: {}, + loading: true, + partial: true, + networkStatus: NetworkStatus.loading, + } satisfies ObservableQuery.Result); + + b.next({ + dataState: 'complete', + data: { user: { name: 'foo' } }, + loading: false, + partial: false, + networkStatus: NetworkStatus.ready, + } satisfies ObservableQuery.Result); + + b.next({ + dataState: 'partial', + data: {}, + loading: true, + partial: true, + networkStatus: NetworkStatus.loading, + } satisfies ObservableQuery.Result); + + b.complete(); + })); + + test('should compile', () => { + TestBed.configureTestingModule({ + providers: [ + provideApollo(() => { + return { + link: new MockLink([]), + cache: new InMemoryCache(), + }; + }), + ], + }); + + const apollo = TestBed.inject(Apollo); + + apollo + .watchQuery({ + query: query, + }) + .valueChanges.pipe( + onlyCompleteData(), + map(result => result.data.user.name), + ); + + apollo + .watchFragment({ + fragment: fragment, + from: { + __typename: 'User', + id: 1, + }, + }) + .pipe( + onlyCompleteFragment(), + map(result => result.data.user.name), + ); + }); +}); diff --git a/packages/apollo-angular/testing/tests/only-complete.spec.ts b/packages/apollo-angular/testing/tests/only-complete.spec.ts deleted file mode 100644 index dfb9689e6..000000000 --- a/packages/apollo-angular/testing/tests/only-complete.spec.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { onlyComplete } from 'apollo-angular'; -import { Subject } from 'rxjs'; -import { describe, expect, test } from 'vitest'; -import { NetworkStatus, ObservableQuery } from '@apollo/client/core'; - -interface Result { - user: { - name: string; - }; -} - -describe('onlyComplete', () => { - let theUser: Result['user'] | null = null; - let count = 0; - - test('should receive only complete results', () => - new Promise(done => { - const b = new Subject>(); - b.pipe(onlyComplete()).subscribe({ - next: result => { - count++; - theUser = result.data.user; - }, - complete: () => { - expect(count).toBe(1); - expect(theUser).toEqual({ name: 'foo' }); - done(); - }, - }); - - b.next({ - dataState: 'partial', - data: {}, - loading: true, - partial: true, - networkStatus: NetworkStatus.loading, - } satisfies ObservableQuery.Result); - - b.next({ - dataState: 'complete', - data: { user: { name: 'foo' } }, - loading: false, - partial: false, - networkStatus: NetworkStatus.ready, - } satisfies ObservableQuery.Result); - - b.next({ - dataState: 'partial', - data: {}, - loading: true, - partial: true, - networkStatus: NetworkStatus.loading, - } satisfies ObservableQuery.Result); - - b.complete(); - })); -}); diff --git a/packages/demo/src/app/pages/movie/movie-page.component.ts b/packages/demo/src/app/pages/movie/movie-page.component.ts index 94e841b31..25aacc2b1 100644 --- a/packages/demo/src/app/pages/movie/movie-page.component.ts +++ b/packages/demo/src/app/pages/movie/movie-page.component.ts @@ -1,4 +1,4 @@ -import { Apollo, gql, onlyComplete } from 'apollo-angular'; +import { Apollo, gql, onlyCompleteData } from 'apollo-angular'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { AsyncPipe } from '@angular/common'; @@ -72,7 +72,7 @@ export class MoviePageComponent implements OnInit { notifyOnNetworkStatusChange: false, }) .valueChanges.pipe( - onlyComplete(), + onlyCompleteData(), map(result => result.data.film), ); } diff --git a/packages/demo/src/app/pages/movies/movies-page.component.ts b/packages/demo/src/app/pages/movies/movies-page.component.ts index 0ad79a82d..220936707 100644 --- a/packages/demo/src/app/pages/movies/movies-page.component.ts +++ b/packages/demo/src/app/pages/movies/movies-page.component.ts @@ -1,4 +1,4 @@ -import { Apollo, gql, onlyComplete } from 'apollo-angular'; +import { Apollo, gql, onlyCompleteData } from 'apollo-angular'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; import { AsyncPipe } from '@angular/common'; @@ -59,7 +59,7 @@ export class MoviesPageComponent implements OnInit { notifyOnNetworkStatusChange: false, }) .valueChanges.pipe( - onlyComplete(), + onlyCompleteData(), map(result => result.data.allFilms.films), ); }