Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic polling query alignment #403

Merged
merged 16 commits into from
Jul 20, 2016
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,31 @@
Expect active development and potentially significant breaking changes in the `0.x` track. We'll try to be diligent about releasing a `1.0` version in a timely fashion (ideally within 3 to 6 months), to signal the start of a more stable API.

### vNEXT
<<<<<<< HEAD
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like we have committed a merge conflict

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

- Added the `batchInterval` option to ApolloClient that allows you to specify the width of the batching interval as per your app's needs. [Issue #394](https://github.com/apollostack/apollo-client/issues/394) and [PR #395](https://github.com/apollostack/apollo-client/pull/395).
- Stringify `storeObj` for error message in `diffFieldAgainstStore`.
- Fix map function returning `undefined` in `removeRefsFromStoreObj`. [PR #393](https://github.com/apollostack/apollo-client/pull/393)
- Added a "noFetch" option to WatchQueryOptions that only returns available data from the local store (even it is incomplete). [Issue #225](https://github.com/apollostack/apollo-client/issues/225) and [PR #385](https://github.com/apollostack/apollo-client/pull/385).
- Integrated the scheduler so that polling queries on the same polling interval are batched together. [PR #403](https://github.com/apollostack/apollo-client/pull/403) and [Issue #401](https://github.com/apollostack/apollo-client/issues/401).
=======

### v0.4.4

- Make sure query transformers are also applied to named fragments, and new methods that allow transforming query document with multiple query transformers. [Issue #373](https://github.com/apollostack/apollo-client/issues/373) [PR #412](https://github.com/apollostack/apollo-client/pull/412)

### v0.4.3

- Introduce a new (preferable) way to express how the mutation result should be incorporated into the store and update watched queries results: `updateQueries`. [PR #404](https://github.com/apollostack/apollo-client/pull/404).
- Writing query results to store no longer creates new objects (and new references) in cases when the new value is identical to the old value in the store.

### v0.4.2

- Added the `batchInterval` option to ApolloClient that allows you to specify the width of the batching interval as per your app's needs. [Issue #394](https://github.com/apollostack/apollo-client/issues/394) and [PR #395](https://github.com/apollostack/apollo-client/pull/395).
- Stringify `storeObj` for error message in `diffFieldAgainstStore`.
- Fix map function returning `undefined` in `removeRefsFromStoreObj`. [PR #393](https://github.com/apollostack/apollo-client/pull/393)
- Added deep result comparison so that observers are only fired when the data associated with a particular query changes. This change eliminates unnecessary re-renders and improves UI performance. [PR #402](https://github.com/apollostack/apollo-client/pull/402) and [Issue #400](https://github.com/apollostack/apollo-client/issues/400).
- Added a "noFetch" option to WatchQueryOptions that only returns available data from the local store (even it is incomplete). The `ObservableQuery` returned from calling `watchQuery` now has `options`, `queryManager`, and `queryId`. The `queryId` can be used to read directly from the state of `apollo.queries`. [Issue #225](https://github.com/apollostack/apollo-client/issues/225), [Issue #342](https://github.com/apollostack/apollo-client/issues/342), and [PR #385](https://github.com/apollostack/apollo-client/pull/385).
>>>>>>> 378cbbe6054d0bae2b4ede2387d19dc2d1927567

### v0.4.1

Expand Down
57 changes: 40 additions & 17 deletions src/ObservableQuery.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,49 @@
import assign = require('lodash.assign');
import { WatchQueryOptions } from './watchQueryOptions';

import { Observable, Observer } from './util/Observable';

import { ApolloQueryResult } from './index';
import {
QueryScheduler,
} from './scheduler';

import { WatchQueryOptions } from './watchQueryOptions';
import {
QueryManager,
} from './QueryManager';

import { QueryManager } from './QueryManager';
import {
ApolloQueryResult,
} from './index';

import assign = require('lodash.assign');

export class ObservableQuery extends Observable<ApolloQueryResult> {
public refetch: (variables?: any) => Promise<ApolloQueryResult>;
public stopPolling: () => void;
public startPolling: (p: number) => void;
public options: WatchQueryOptions;
public queryManager: QueryManager;
public queryId: string;
private queryId: string;
private scheduler: QueryScheduler;
private queryManager: QueryManager;

constructor({
queryManager,
scheduler,
options,
shouldSubscribe = true,
}: {
queryManager: QueryManager,
scheduler: QueryScheduler,
options: WatchQueryOptions,
shouldSubscribe?: boolean,
}) {

const queryManager = scheduler.queryManager;
const queryId = queryManager.generateQueryId();
const isPollingQuery = !!options.pollInterval;

const subscriberFunction = (observer: Observer<ApolloQueryResult>) => {
const retQuerySubscription = {
unsubscribe: () => {
if (isPollingQuery) {
scheduler.stopPollingQuery(queryId);
}
queryManager.stopQuery(queryId);
},
};
Expand All @@ -39,15 +53,23 @@ export class ObservableQuery extends Observable<ApolloQueryResult> {
queryManager.addQuerySubscription(queryId, retQuerySubscription);
}

if (isPollingQuery) {
this.scheduler.startPollingQuery(
options,
queryId
);
}
queryManager.startQuery(
queryId,
options,
queryManager.queryListenerForObserver(queryId, options, observer)
);

return retQuerySubscription;
};
super(subscriberFunction);
this.options = options;
this.scheduler = scheduler;
this.queryManager = queryManager;
this.queryId = queryId;

Expand All @@ -65,21 +87,22 @@ export class ObservableQuery extends Observable<ApolloQueryResult> {
};

this.stopPolling = () => {
if (this.queryManager.pollingTimers[this.queryId]) {
clearInterval(this.queryManager.pollingTimers[this.queryId]);
this.queryManager.stopQuery(this.queryId);
if (isPollingQuery) {
this.scheduler.stopPollingQuery(this.queryId);
}
};

this.startPolling = (pollInterval) => {
if (this.options.noFetch) {
throw new Error('noFetch option should not use query polling.');
}
this.queryManager.pollingTimers[this.queryId] = setInterval(() => {
const pollingOptions = assign({}, this.options) as WatchQueryOptions;
// subsequent fetches from polling always reqeust new data
pollingOptions.forceFetch = true;
this.queryManager.fetchQuery(this.queryId, pollingOptions);
}, pollInterval);

if (isPollingQuery) {
this.scheduler.stopPollingQuery(this.queryId);
}
options.pollInterval = pollInterval;
this.scheduler.startPollingQuery(this.options, this.queryId, false);
};
}

Expand Down
109 changes: 1 addition & 108 deletions src/QueryManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
} from './networkInterface';

import forOwn = require('lodash.forown');
import assign = require('lodash.assign');
import isEqual = require('lodash.isequal');

import {
Expand Down Expand Up @@ -83,113 +82,7 @@ import {

import { WatchQueryOptions } from './watchQueryOptions';

export class ObservableQuery extends Observable<ApolloQueryResult> {
public refetch: (variables?: any) => Promise<ApolloQueryResult>;
public stopPolling: () => void;
public startPolling: (p: number) => void;
public options: WatchQueryOptions;
private queryId: string;
private scheduler: QueryScheduler;
private queryManager: QueryManager;

constructor({
scheduler,
options,
shouldSubscribe = true,
}: {
scheduler: QueryScheduler,
options: WatchQueryOptions,
shouldSubscribe?: boolean,
}) {
const queryManager = scheduler.queryManager;
const queryId = queryManager.generateQueryId();
const isPollingQuery = !!options.pollInterval;

const subscriberFunction = (observer: Observer<ApolloQueryResult>) => {
const retQuerySubscription = {
unsubscribe: () => {
if (isPollingQuery) {
scheduler.stopPollingQuery(queryId);
}
queryManager.stopQuery(queryId);
},
};

if (shouldSubscribe) {
queryManager.addObservableQuery(queryId, this);
queryManager.addQuerySubscription(queryId, retQuerySubscription);
}

if (isPollingQuery) {
this.scheduler.startPollingQuery(
options,
queryId
);
}
queryManager.startQuery(
queryId,
options,
queryManager.queryListenerForObserver(options, observer)
);

return retQuerySubscription;
};
super(subscriberFunction);
this.options = options;
this.scheduler = scheduler;
this.queryManager = queryManager;
this.queryId = queryId;

this.refetch = (variables?: any) => {
// If no new variables passed, use existing variables
variables = variables || this.options.variables;
if (this.options.noFetch) {
throw new Error('noFetch option should not use query refetch.');
}
// Use the same options as before, but with new variables and forceFetch true
return this.queryManager.fetchQuery(this.queryId, assign(this.options, {
forceFetch: true,
variables,
}) as WatchQueryOptions);
};

this.stopPolling = () => {
this.queryManager.stopQuery(this.queryId);
if (isPollingQuery) {
this.scheduler.stopPollingQuery(this.queryId);
}
};

this.startPolling = (pollInterval) => {
if (this.options.noFetch) {
throw new Error('noFetch option should not use query polling.');
}

if (isPollingQuery) {
this.scheduler.stopPollingQuery(this.queryId);
}
options.pollInterval = pollInterval;
this.scheduler.startPollingQuery(this.options, this.queryId, false);
};
}

public result(): Promise<ApolloQueryResult> {
return new Promise((resolve, reject) => {
const subscription = this.subscribe({
next(result) {
resolve(result);
setTimeout(() => {
subscription.unsubscribe();
}, 0);
},
error(error) {
reject(error);
},
});
});
}

}
import { ObservableQuery } from './ObservableQuery';

export type QueryListener = (queryStoreValue: QueryStoreValue) => void;

Expand Down