Skip to content

Commit

Permalink
Merge pull request #3140 from excitement-engineer/improve-types
Browse files Browse the repository at this point in the history
Improved type definitions for the observable query
  • Loading branch information
hwillson authored Jun 1, 2018
2 parents cd18529 + a72eefd commit aee073e
Show file tree
Hide file tree
Showing 5 changed files with 104 additions and 79 deletions.
3 changes: 1 addition & 2 deletions docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,7 @@
},
"scripts": {
"start": "npm run build && chexo apollo-hexo-config -- server",
"types":
"cd ../packages/apollo-client && typedoc --json ../../docs/docs.json --ignoreCompilerErrors ./src/index.ts",
"types": "cd ../packages/apollo-client && typedoc --json ../../docs/docs.json --ignoreCompilerErrors ./src/index.ts",
"build": "npm run types && chexo apollo-hexo-config -- generate",
"clean": "hexo clean",
"test": "npm run clean; npm run build"
Expand Down
9 changes: 8 additions & 1 deletion packages/apollo-client/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# CHANGELOG

### vNext

- Typescript improvements. Made observable query parameterized on data and
variables: `ObservableQuery<TData, TVariables>`
[PR#3140](https://github.com/apollographql/apollo-client/pull/3140)

### 2.3.2

- Fix SSR and `cache-and-network` fetch policy
Expand Down Expand Up @@ -30,7 +36,8 @@

### 2.3.0
- fixed edge case bug of changing fetchPolicies right after resetStore with no variables present
- Various optimizations for cache read performance [#3300](https://github.com/apollographql/apollo-client/pull/3300)
- Various optimizations for cache read performance
[#3300](https://github.com/apollographql/apollo-client/pull/3300)

### 2.2.8
- Added the graphQLResultHasError in QueryManager.ts to check not only if result.errors is null, but also empty.
Expand Down
137 changes: 75 additions & 62 deletions packages/apollo-client/src/core/ObservableQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { QueryScheduler } from '../scheduler/scheduler';
import { ApolloError } from '../errors/ApolloError';

import { QueryManager } from './QueryManager';
import { ApolloQueryResult, FetchType } from './types';
import { ApolloQueryResult, FetchType, OperationVariables } from './types';
import {
ModifiableWatchQueryOptions,
WatchQueryOptions,
Expand All @@ -33,18 +33,21 @@ export type ApolloCurrentResult<T> = {
partial?: boolean;
};

export interface FetchMoreOptions {
export interface FetchMoreOptions<
TData = any,
TVariables = OperationVariables
> {
updateQuery: (
previousQueryResult: { [key: string]: any },
previousQueryResult: TData,
options: {
fetchMoreResult?: { [key: string]: any };
variables: { [key: string]: any };
fetchMoreResult?: TData;
variables?: TVariables;
},
) => Object;
) => TData;
}

export interface UpdateQueryOptions {
variables?: Object;
export interface UpdateQueryOptions<TVariables> {
variables?: TVariables;
}

export const hasError = (
Expand All @@ -57,24 +60,27 @@ export const hasError = (
policy === 'none') ||
storeValue.networkError);

export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
public options: WatchQueryOptions;
export class ObservableQuery<
TData = any,
TVariables = OperationVariables
> extends Observable<ApolloQueryResult<TData>> {
public options: WatchQueryOptions<TVariables>;
public queryId: string;
/**
*
* The current value of the variables for this query. Can change.
*/
public variables: { [key: string]: any };
public variables: TVariables;

private isCurrentlyPolling: boolean;
private shouldSubscribe: boolean;
private isTornDown: boolean;
private scheduler: QueryScheduler<any>;
private queryManager: QueryManager<any>;
private observers: Observer<ApolloQueryResult<T>>[];
private observers: Observer<ApolloQueryResult<TData>>[];
private subscriptionHandles: Subscription[];

private lastResult: ApolloQueryResult<T>;
private lastResult: ApolloQueryResult<TData>;
private lastError: ApolloError;

constructor({
Expand All @@ -83,10 +89,10 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
shouldSubscribe = true,
}: {
scheduler: QueryScheduler<any>;
options: WatchQueryOptions;
options: WatchQueryOptions<TVariables>;
shouldSubscribe?: boolean;
}) {
super((observer: Observer<ApolloQueryResult<T>>) =>
super((observer: Observer<ApolloQueryResult<TData>>) =>
this.onSubscribe(observer),
);

Expand All @@ -96,7 +102,7 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {

// query information
this.options = options;
this.variables = options.variables || {};
this.variables = options.variables || ({} as TVariables);
this.queryId = scheduler.queryManager.generateQueryId();
this.shouldSubscribe = shouldSubscribe;

Expand All @@ -109,11 +115,11 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
this.subscriptionHandles = [];
}

public result(): Promise<ApolloQueryResult<T>> {
public result(): Promise<ApolloQueryResult<TData>> {
const that = this;
return new Promise((resolve, reject) => {
let subscription: Subscription;
const observer: Observer<ApolloQueryResult<T>> = {
const observer: Observer<ApolloQueryResult<TData>> = {
next(result) {
resolve(result);

Expand Down Expand Up @@ -149,7 +155,7 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
* `partial` lets you know if the result from the local cache is complete or partial
* @return {result: Object, loading: boolean, networkStatus: number, partial: boolean}
*/
public currentResult(): ApolloCurrentResult<T> {
public currentResult(): ApolloCurrentResult<TData> {
if (this.isTornDown) {
return {
data: this.lastError ? {} : this.lastResult ? this.lastResult.data : {},
Expand Down Expand Up @@ -202,7 +208,7 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
data,
loading: isNetworkRequestInFlight(networkStatus),
networkStatus,
} as ApolloQueryResult<T>;
} as ApolloQueryResult<TData>;

if (
queryStoreValue &&
Expand All @@ -217,12 +223,12 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
this.lastResult = { ...result, stale };
}

return { ...result, partial } as ApolloCurrentResult<T>;
return { ...result, partial } as ApolloCurrentResult<TData>;
}

// Returns the last result that observer.next was called with. This is not the same as
// currentResult! If you're not sure which you need, then you probably need currentResult.
public getLastResult(): ApolloQueryResult<T> {
public getLastResult(): ApolloQueryResult<TData> {
return this.lastResult;
}

Expand All @@ -236,7 +242,7 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
this.isTornDown = false;
}

public refetch(variables?: any): Promise<ApolloQueryResult<T>> {
public refetch(variables?: TVariables): Promise<ApolloQueryResult<TData>> {
const { fetchPolicy } = this.options;
// early return if trying to read from cache during refetch
if (fetchPolicy === 'cache-only') {
Expand All @@ -249,18 +255,16 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {

if (!isEqual(this.variables, variables)) {
// update observable variables
this.variables = {
...this.variables,
...variables,
};
this.variables = Object.assign({}, this.variables, variables);
}

if (!isEqual(this.options.variables, this.variables)) {
// Update the existing options with new variables
this.options.variables = {
...this.options.variables,
...this.variables,
};
this.options.variables = Object.assign(
{},
this.options.variables,
this.variables,
);
}

// Override fetchPolicy for this call only
Expand All @@ -278,9 +282,10 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
.then(result => maybeDeepFreeze(result));
}

public fetchMore(
fetchMoreOptions: FetchMoreQueryOptions & FetchMoreOptions,
): Promise<ApolloQueryResult<T>> {
public fetchMore<K extends keyof TVariables>(
fetchMoreOptions: FetchMoreQueryOptions<TVariables, K> &
FetchMoreOptions<TData, TVariables>,
): Promise<ApolloQueryResult<TData>> {
// early return if no update Query
if (!fetchMoreOptions.updateQuery) {
throw new Error(
Expand All @@ -302,10 +307,11 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
combinedOptions = {
...this.options,
...fetchMoreOptions,
variables: {
...this.variables,
...fetchMoreOptions.variables,
},
variables: Object.assign(
{},
this.variables,
fetchMoreOptions.variables,
),
};
}

Expand All @@ -321,19 +327,21 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
.then(fetchMoreResult => {
this.updateQuery((previousResult: any) =>
fetchMoreOptions.updateQuery(previousResult, {
fetchMoreResult: fetchMoreResult.data,
fetchMoreResult: fetchMoreResult.data as TData,
variables: combinedOptions.variables,
}),
);

return fetchMoreResult as ApolloQueryResult<T>;
return fetchMoreResult as ApolloQueryResult<TData>;
});
}

// XXX the subscription variables are separate from the query variables.
// if you want to update subscription variables, right now you have to do that separately,
// and you can only do it by stopping the subscription and then subscribing again with new variables.
public subscribeToMore(options: SubscribeToMoreOptions): () => void {
public subscribeToMore(
options: SubscribeToMoreOptions<TData, TVariables>,
): () => void {
const subscription = this.queryManager
.startGraphQLSubscription({
query: options.document,
Expand All @@ -342,11 +350,14 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
.subscribe({
next: data => {
if (options.updateQuery) {
this.updateQuery((previous: Object, { variables }) =>
(options.updateQuery as UpdateQueryFn)(previous, {
subscriptionData: data,
variables,
}),
this.updateQuery((previous, { variables }) =>
(options.updateQuery as UpdateQueryFn<TData, TVariables>)(
previous,
{
subscriptionData: data as { data: TData },
variables,
},
),
);
}
},
Expand Down Expand Up @@ -374,12 +385,11 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
// will return null immediately.
public setOptions(
opts: ModifiableWatchQueryOptions,
): Promise<ApolloQueryResult<T>> {
): Promise<ApolloQueryResult<TData>> {
const oldOptions = this.options;
this.options = {
...this.options,
...opts,
} as WatchQueryOptions;
this.options = Object.assign({}, this.options, opts) as WatchQueryOptions<
TVariables
>;

if (opts.pollInterval) {
this.startPolling(opts.pollInterval);
Expand All @@ -398,7 +408,7 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
false;

return this.setVariables(
this.options.variables,
this.options.variables as TVariables,
tryFetch,
opts.fetchResults,
);
Expand All @@ -425,10 +435,10 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
*
*/
public setVariables(
variables: any,
variables: TVariables,
tryFetch: boolean = false,
fetchResults = true,
): Promise<ApolloQueryResult<T>> {
): Promise<ApolloQueryResult<TData>> {
// since setVariables restarts the subscription, we reset the tornDown status
this.isTornDown = false;

Expand Down Expand Up @@ -462,7 +472,10 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
}

public updateQuery(
mapFn: (previousQueryResult: any, options: UpdateQueryOptions) => any,
mapFn: (
previousQueryResult: TData,
options: UpdateQueryOptions<TVariables>,
) => TData,
): void {
const {
previousResult,
Expand All @@ -471,7 +484,7 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
} = this.queryManager.getQueryWithPreviousResult(this.queryId);

const newResult = tryFunctionOrLogError(() =>
mapFn(previousResult, { variables }),
mapFn(previousResult, { variables: variables as TVariables }),
);

if (newResult) {
Expand Down Expand Up @@ -511,7 +524,7 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
this.scheduler.startPollingQuery(this.options, this.queryId);
}

private onSubscribe(observer: Observer<ApolloQueryResult<T>>) {
private onSubscribe(observer: Observer<ApolloQueryResult<TData>>) {
// Zen Observable has its own error function, in order to log correctly
// we need to declare a custom error if nothing is passed
if (
Expand Down Expand Up @@ -546,7 +559,7 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {

private setUpQuery() {
if (this.shouldSubscribe) {
this.queryManager.addObservableQuery<T>(this.queryId, this);
this.queryManager.addObservableQuery<TData>(this.queryId, this);
}

if (!!this.options.pollInterval) {
Expand All @@ -560,11 +573,11 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
}

this.isCurrentlyPolling = true;
this.scheduler.startPollingQuery<T>(this.options, this.queryId);
this.scheduler.startPollingQuery<TData>(this.options, this.queryId);
}

const observer: Observer<ApolloQueryResult<T>> = {
next: (result: ApolloQueryResult<T>) => {
const observer: Observer<ApolloQueryResult<TData>> = {
next: (result: ApolloQueryResult<TData>) => {
this.lastResult = result;
this.observers.forEach(obs => obs.next && obs.next(result));
},
Expand All @@ -574,7 +587,7 @@ export class ObservableQuery<T> extends Observable<ApolloQueryResult<T>> {
},
};

this.queryManager.startQuery<T>(
this.queryManager.startQuery<TData>(
this.queryId,
this.options,
this.queryManager.queryListenerForObserver(
Expand Down
2 changes: 2 additions & 0 deletions packages/apollo-client/src/core/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export type QueryListener = (
newData?: any,
) => void;

export type OperationVariables = { [key: string]: any };

export type PureQueryOptions = {
query: DocumentNode;
variables?: { [key: string]: any };
Expand Down
Loading

0 comments on commit aee073e

Please sign in to comment.