Skip to content

Commit 34ab338

Browse files
committed
WIP apollo-client v4
Work in process to migrate to the future apollo-client v4 and its native RxJS support via apollographql/apollo-client#12384
1 parent 53d4189 commit 34ab338

File tree

13 files changed

+56
-128
lines changed

13 files changed

+56
-128
lines changed

packages/apollo-angular/headers/tests/index.spec.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
import { of } from 'rxjs';
12
import { describe, expect, test } from 'vitest';
23
import { HttpHeaders } from '@angular/common/http';
3-
import { ApolloLink, execute, gql, Observable as LinkObservable } from '@apollo/client/core';
4+
import { ApolloLink, execute, gql } from '@apollo/client/core';
45
import { httpHeaders } from '../src';
56

67
const query = gql`
@@ -24,7 +25,7 @@ describe('httpHeaders', () => {
2425
expect(headers instanceof HttpHeaders).toBe(true);
2526
expect(headers.get('Authorization')).toBe('Bearer Foo');
2627

27-
return LinkObservable.of({ data });
28+
return of({ data });
2829
});
2930

3031
const link = headersLink.concat(mockLink);
@@ -51,7 +52,7 @@ describe('httpHeaders', () => {
5152

5253
expect(headers).toBeUndefined();
5354

54-
return LinkObservable.of({ data });
55+
return of({ data });
5556
});
5657

5758
const link = headersLink.concat(mockLink);

packages/apollo-angular/http/src/http-batch-link.ts

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,8 @@
11
import { print } from 'graphql';
2+
import { Observable } from 'rxjs';
23
import { HttpClient, HttpHeaders } from '@angular/common/http';
34
import { Injectable } from '@angular/core';
4-
import {
5-
ApolloLink,
6-
FetchResult,
7-
Observable as LinkObservable,
8-
Operation,
9-
} from '@apollo/client/core';
5+
import { ApolloLink, FetchResult, Operation } from '@apollo/client/core';
106
import { BatchHandler, BatchLink } from '@apollo/client/link/batch';
117
import { BatchOptions, Body, Context, OperationPrinter, Options, Request } from './types';
128
import { createHeadersWithClientAwareness, fetch, mergeHeaders, prioritize } from './utils';
@@ -53,7 +49,7 @@ export class HttpBatchLinkHandler extends ApolloLink {
5349
}
5450

5551
const batchHandler: BatchHandler = (operations: Operation[]) => {
56-
return new LinkObservable((observer: any) => {
52+
return new Observable((observer: any) => {
5753
const body = this.createBody(operations);
5854
const headers = this.createHeaders(operations);
5955
const { method, uri, withCredentials } = this.createOptions(operations);
@@ -175,7 +171,7 @@ export class HttpBatchLinkHandler extends ApolloLink {
175171
return prioritize(context.uri, this.options.uri, '') + opts;
176172
}
177173

178-
public request(op: Operation): LinkObservable<FetchResult> | null {
174+
public request(op: Operation): Observable<FetchResult> | null {
179175
return this.batcher.request(op);
180176
}
181177
}

packages/apollo-angular/http/src/http-link.ts

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
11
import { print } from 'graphql';
2+
import { Observable } from 'rxjs';
23
import { HttpClient } from '@angular/common/http';
34
import { Injectable } from '@angular/core';
4-
import {
5-
ApolloLink,
6-
FetchResult,
7-
Observable as LinkObservable,
8-
Operation,
9-
} from '@apollo/client/core';
5+
import { ApolloLink, FetchResult, Operation } from '@apollo/client/core';
106
import { pick } from './http-batch-link';
117
import { Body, Context, OperationPrinter, Options, Request } from './types';
128
import { createHeadersWithClientAwareness, fetch, mergeHeaders } from './utils';
139

1410
// XXX find a better name for it
1511
export class HttpLinkHandler extends ApolloLink {
16-
public requester: (operation: Operation) => LinkObservable<FetchResult> | null;
12+
public requester: (operation: Operation) => Observable<FetchResult> | null;
1713
private print: OperationPrinter = print;
1814

1915
constructor(
@@ -27,7 +23,7 @@ export class HttpLinkHandler extends ApolloLink {
2723
}
2824

2925
this.requester = (operation: Operation) =>
30-
new LinkObservable((observer: any) => {
26+
new Observable((observer: any) => {
3127
const context: Context = operation.getContext();
3228

3329
let method = pick(context, this.options, 'method');
@@ -89,7 +85,7 @@ export class HttpLinkHandler extends ApolloLink {
8985
});
9086
}
9187

92-
public request(op: Operation): LinkObservable<FetchResult> | null {
88+
public request(op: Operation): Observable<FetchResult> | null {
9389
return this.requester(op);
9490
}
9591
}

packages/apollo-angular/http/tests/http-link.spec.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { print, stripIgnoredCharacters } from 'graphql';
2-
import { mergeMap } from 'rxjs/operators';
2+
import { map, mergeMap } from 'rxjs/operators';
33
import { afterEach, beforeEach, describe, expect, test } from 'vitest';
44
import { HttpHeaders, provideHttpClient } from '@angular/common/http';
55
import { HttpTestingController, provideHttpClientTesting } from '@angular/common/http/testing';
@@ -512,14 +512,16 @@ describe('HttpLink', () => {
512512
test('should set response in context', () =>
513513
new Promise<void>(done => {
514514
const afterware = new ApolloLink((op, forward) => {
515-
return forward(op).map(response => {
516-
const context = op.getContext();
515+
return forward(op).pipe(
516+
map(response => {
517+
const context = op.getContext();
517518

518-
expect(context.response).toBeDefined();
519-
done();
519+
expect(context.response).toBeDefined();
520+
done();
520521

521-
return response;
522-
});
522+
return response;
523+
}),
524+
);
523525
});
524526
const link = afterware.concat(
525527
httpLink.create({

packages/apollo-angular/src/apollo.ts

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { from, Observable } from 'rxjs';
1+
import { Observable } from 'rxjs';
22
import { Inject, Injectable, NgZone, Optional } from '@angular/core';
33
import type {
44
ApolloClientOptions,
@@ -23,18 +23,16 @@ import type {
2323
WatchFragmentOptions,
2424
WatchQueryOptions,
2525
} from './types';
26-
import { fixObservable, fromPromise, useMutationLoading, wrapWithZone } from './utils';
26+
import { fromLazyPromise, useMutationLoading, wrapWithZone } from './utils';
2727

2828
export class ApolloBase<TCacheShape = any> {
29-
private useInitialLoading: boolean;
3029
private useMutationLoading: boolean;
3130

3231
constructor(
3332
protected readonly ngZone: NgZone,
3433
protected readonly flags?: Flags,
3534
protected _client?: ApolloClient<TCacheShape>,
3635
) {
37-
this.useInitialLoading = flags?.useInitialLoading ?? false;
3836
this.useMutationLoading = flags?.useMutationLoading ?? false;
3937
}
4038

@@ -46,24 +44,22 @@ export class ApolloBase<TCacheShape = any> {
4644
...options,
4745
}) as ObservableQuery<TData, TVariables>,
4846
this.ngZone,
49-
{
50-
useInitialLoading: this.useInitialLoading,
51-
...options,
52-
},
5347
);
5448
}
5549

5650
public query<T, V extends OperationVariables = EmptyObject>(
5751
options: QueryOptions<V, T>,
5852
): Observable<ApolloQueryResult<T>> {
59-
return fromPromise<ApolloQueryResult<T>>(() => this.ensureClient().query<T, V>({ ...options }));
53+
return fromLazyPromise<ApolloQueryResult<T>>(() =>
54+
this.ensureClient().query<T, V>({ ...options }),
55+
);
6056
}
6157

6258
public mutate<T, V extends OperationVariables = EmptyObject>(
6359
options: MutationOptions<T, V>,
6460
): Observable<MutationResult<T>> {
6561
return useMutationLoading(
66-
fromPromise(() => this.ensureClient().mutate<T, V>({ ...options })),
62+
fromLazyPromise(() => this.ensureClient().mutate<T, V>({ ...options })),
6763
options.useMutationLoading ?? this.useMutationLoading,
6864
);
6965
}
@@ -75,9 +71,7 @@ export class ApolloBase<TCacheShape = any> {
7571
options: WatchFragmentOptions<TFragmentData, TVariables>,
7672
extra?: ExtraSubscriptionOptions,
7773
): Observable<WatchFragmentResult<TFragmentData>> {
78-
const obs = from(
79-
fixObservable(this.ensureClient().watchFragment<TFragmentData, TVariables>({ ...options })),
80-
);
74+
const obs = this.ensureClient().watchFragment<TFragmentData, TVariables>({ ...options });
8175

8276
return extra && extra.useZone !== true ? obs : wrapWithZone(obs, this.ngZone);
8377
}
@@ -86,7 +80,7 @@ export class ApolloBase<TCacheShape = any> {
8680
options: SubscriptionOptions<V, T>,
8781
extra?: ExtraSubscriptionOptions,
8882
): Observable<FetchResult<T>> {
89-
const obs = from(fixObservable(this.ensureClient().subscribe<T, V>({ ...options })));
83+
const obs = this.ensureClient().subscribe<T, V>({ ...options });
9084

9185
return extra && extra.useZone !== true ? obs : wrapWithZone(obs, this.ngZone);
9286
}

packages/apollo-angular/src/query-ref.ts

Lines changed: 4 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { from, Observable } from 'rxjs';
1+
import { Observable } from 'rxjs';
22
import { NgZone } from '@angular/core';
33
import type {
44
ApolloQueryResult,
@@ -10,38 +10,8 @@ import type {
1010
TypedDocumentNode,
1111
Unmasked,
1212
} from '@apollo/client/core';
13-
import { NetworkStatus } from '@apollo/client/core';
14-
import { EmptyObject, WatchQueryOptions } from './types';
15-
import { fixObservable, wrapWithZone } from './utils';
16-
17-
function useInitialLoading<T, V extends OperationVariables>(obsQuery: ObservableQuery<T, V>) {
18-
return function useInitialLoadingOperator<T>(source: Observable<T>): Observable<T> {
19-
return new Observable(function useInitialLoadingSubscription(subscriber) {
20-
const currentResult = obsQuery.getCurrentResult();
21-
const { loading, errors, error, partial, data } = currentResult;
22-
const { partialRefetch, fetchPolicy } = obsQuery.options;
23-
24-
const hasError = errors || error;
25-
26-
if (
27-
partialRefetch &&
28-
partial &&
29-
(!data || Object.keys(data).length === 0) &&
30-
fetchPolicy !== 'cache-only' &&
31-
!loading &&
32-
!hasError
33-
) {
34-
subscriber.next({
35-
...currentResult,
36-
loading: true,
37-
networkStatus: NetworkStatus.loading,
38-
} as any);
39-
}
40-
41-
return source.subscribe(subscriber);
42-
});
43-
};
44-
}
13+
import { EmptyObject } from './types';
14+
import { fromObservableQuery, wrapWithZone } from './utils';
4515

4616
export type QueryRefFromDocument<T extends TypedDocumentNode> =
4717
T extends TypedDocumentNode<infer R, infer V> ? QueryRef<R, V & OperationVariables> : never;
@@ -53,13 +23,8 @@ export class QueryRef<TData, TVariables extends OperationVariables = EmptyObject
5323
constructor(
5424
private readonly obsQuery: ObservableQuery<TData, TVariables>,
5525
ngZone: NgZone,
56-
options: WatchQueryOptions<TVariables, TData>,
5726
) {
58-
const wrapped = wrapWithZone(from(fixObservable(this.obsQuery)), ngZone);
59-
60-
this.valueChanges = options.useInitialLoading
61-
? wrapped.pipe(useInitialLoading(this.obsQuery))
62-
: wrapped;
27+
this.valueChanges = wrapWithZone(fromObservableQuery(this.obsQuery), ngZone);
6328
this.queryId = this.obsQuery.queryId;
6429
}
6530

packages/apollo-angular/src/types.ts

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -44,15 +44,7 @@ export interface SubscriptionOptionsAlone<TVariables = EmptyObject, TData = any>
4444
extends Omit<CoreSubscriptionOptions<TVariables, TData>, 'query' | 'variables'> {}
4545

4646
export interface WatchQueryOptions<TVariables extends OperationVariables = EmptyObject, TData = any>
47-
extends CoreWatchQueryOptions<TVariables, TData> {
48-
/**
49-
* Observable starts with `{ loading: true }`.
50-
* There's a big chance the next major version will enable that by default.
51-
*
52-
* Disabled by default
53-
*/
54-
useInitialLoading?: boolean;
55-
}
47+
extends CoreWatchQueryOptions<TVariables, TData> {}
5648

5749
export interface MutationOptions<TData = any, TVariables = EmptyObject>
5850
extends CoreMutationOptions<TData, TVariables> {
@@ -71,13 +63,6 @@ export interface WatchFragmentOptions<TData = any, TVariables = EmptyObject>
7163
export type NamedOptions = Record<string, ApolloClientOptions<any>>;
7264

7365
export type Flags = {
74-
/**
75-
* Observable starts with `{ loading: true }`.
76-
* There's a big chance the next major version will enable that by default.
77-
*
78-
* Disabled by default
79-
*/
80-
useInitialLoading?: boolean;
8166
/**
8267
* Observable starts with `{ loading: true }`.
8368
*

packages/apollo-angular/src/utils.ts

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
1-
import type { SchedulerAction, SchedulerLike, Subscription } from 'rxjs';
2-
import { Observable, observable, queueScheduler } from 'rxjs';
1+
import { Observable, queueScheduler, SchedulerAction, SchedulerLike, Subscription } from 'rxjs';
32
import { map, observeOn, startWith } from 'rxjs/operators';
43
import { NgZone } from '@angular/core';
5-
import type {
6-
Observable as AObservable,
7-
ApolloQueryResult,
8-
FetchResult,
9-
ObservableQuery,
10-
} from '@apollo/client/core';
4+
import type { ApolloQueryResult, FetchResult, ObservableQuery } from '@apollo/client/core';
115
import { MutationResult } from './types';
126

13-
export function fromPromise<T>(promiseFn: () => Promise<T>): Observable<T> {
7+
/**
8+
* Like RxJS's `fromPromise()`, but starts the promise only when the observable is subscribed to.
9+
*/
10+
export function fromLazyPromise<T>(promiseFn: () => Promise<T>): Observable<T> {
1411
return new Observable<T>(subscriber => {
1512
promiseFn().then(
1613
result => {
@@ -54,7 +51,7 @@ export function useMutationLoading<T>(source: Observable<FetchResult<T>>, enable
5451
export class ZoneScheduler implements SchedulerLike {
5552
constructor(private readonly zone: NgZone) {}
5653

57-
public now = Date.now ? Date.now : () => +new Date();
54+
public readonly now = Date.now;
5855

5956
public schedule<T>(
6057
work: (this: SchedulerAction<T>, state?: T) => void,
@@ -65,16 +62,12 @@ export class ZoneScheduler implements SchedulerLike {
6562
}
6663
}
6764

68-
// XXX: Apollo's QueryObservable is not compatible with RxJS
69-
// TODO: remove it in one of future releases
70-
// https://github.com/ReactiveX/rxjs/blob/9fb0ce9e09c865920cf37915cc675e3b3a75050b/src/internal/util/subscribeTo.ts#L32
71-
export function fixObservable<T>(obs: ObservableQuery<T>): Observable<ApolloQueryResult<T>>;
72-
export function fixObservable<T>(obs: AObservable<T>): Observable<T>;
73-
export function fixObservable<T>(
74-
obs: AObservable<T> | ObservableQuery<T>,
75-
): Observable<ApolloQueryResult<T>> | Observable<T> {
76-
(obs as any)[observable] = () => obs;
77-
return obs as any;
65+
export function fromObservableQuery<TData>(
66+
obsQuery: ObservableQuery<TData, any>,
67+
): Observable<ApolloQueryResult<TData>> {
68+
return new Observable(subscriber => {
69+
return obsQuery.subscribe(subscriber);
70+
});
7871
}
7972

8073
export function wrapWithZone<T>(obs: Observable<T>, ngZone: NgZone): Observable<T> {

packages/apollo-angular/testing/src/backend.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { DocumentNode, print } from 'graphql';
2-
import { Observer } from 'rxjs';
2+
import { Observable, Observer } from 'rxjs';
33
import { Injectable } from '@angular/core';
4-
import { FetchResult, Observable as LinkObservable } from '@apollo/client/core';
4+
import { FetchResult } from '@apollo/client/core';
55
import { ApolloTestingController, MatchOperation } from './controller';
66
import { Operation, TestOperation } from './operation';
77

@@ -23,8 +23,8 @@ export class ApolloTestingBackend implements ApolloTestingController {
2323
/**
2424
* Handle an incoming operation by queueing it in the list of open operations.
2525
*/
26-
public handle(op: Operation): LinkObservable<FetchResult> {
27-
return new LinkObservable((observer: Observer<any>) => {
26+
public handle(op: Operation): Observable<FetchResult> {
27+
return new Observable((observer: Observer<any>) => {
2828
const testOp = new TestOperation(op, observer);
2929
this.open.push(testOp);
3030
});

packages/apollo-angular/testing/src/module.ts

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,7 @@ export class ApolloTestingModuleCore {
5555
return {
5656
connectToDevTools: false,
5757
link: new ApolloLink(operation => backend.handle(addClient(name, operation))),
58-
cache:
59-
c ||
60-
new InMemoryCache({
61-
addTypename: false,
62-
}),
58+
cache: c || new InMemoryCache(),
6359
};
6460
}
6561

0 commit comments

Comments
 (0)