diff --git a/Changelog.md b/Changelog.md index 313e7adb8f..5b37ee41c6 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,11 @@ 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 1 or 2 months), so that we can take advantage of SemVer to signify breaking changes from that point on. +### v0.3.20 + +- Bug: Fixed loading state on refetch more when data doesn't change +- Feature: added fetchMore [#123](https://github.com/apollostack/react-apollo/pull/123) + ### v0.3.19 - Bug: Retain compatibility with version 0.3.0 of Apollo Client via a backcompat shim. [#109](https://github.com/apollostack/react-apollo/pull/109) diff --git a/global.d.ts b/global.d.ts index 90ee6649b5..ace1751e21 100644 --- a/global.d.ts +++ b/global.d.ts @@ -2,12 +2,12 @@ LODASH */ declare module 'lodash.isobject' { - import main = require('~lodash/index'); + import main = require('lodash'); export = main.isObject; } declare module 'lodash.isequal' { - import main = require('~lodash/index'); + import main = require('lodash'); export = main.isEqual; } @@ -29,6 +29,6 @@ declare module 'hoist-non-react-statics' { } declare module 'lodash.flatten' { - import main = require('~lodash/index'); + import main = require('lodash'); export = main.flatten; } diff --git a/package.json b/package.json index 746c3c3a05..9263589163 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-apollo", - "version": "0.3.19", + "version": "0.3.20", "description": "React data container for Apollo Client", "main": "index.js", "scripts": { @@ -37,7 +37,7 @@ "apollo-client": "^0.1.0 || ^0.2.0 || ^0.3.0 || ^0.4.0" }, "devDependencies": { - "apollo-client": "^0.4.0", + "apollo-client": "^0.4.9", "browserify": "^13.0.0", "chai": "^3.5.0", "chai-as-promised": "^5.2.0", diff --git a/src/connect.tsx b/src/connect.tsx index 759cd61fbe..869645fc3c 100644 --- a/src/connect.tsx +++ b/src/connect.tsx @@ -29,7 +29,7 @@ import ApolloClient, { import { ObservableQuery, -} from 'apollo-client/QueryManager'; +} from 'apollo-client/ObservableQuery'; import { Subscription, @@ -343,7 +343,9 @@ export default function connect(opts?: ConnectOptions) { // has been recieved let refetch, startPolling, - stopPolling; + stopPolling, + fetchMore, + oldData = {}; // since we don't have the query id, we can manually handle // a lifecyle event for loading if this query is refetched @@ -361,26 +363,59 @@ export default function connect(opts?: ConnectOptions) { this.forceRenderChildren(); } + let previousRequest = assign({}, oldData); + return refetchMethod(...args) + .then((result) => { + const { data } = result; + + if (isEqual(data, previousRequest)) { + this.data[dataKey] = assign(this.data[dataKey], { + loading: false, + }); + this.hasQueryDataChanged = true; + + if (this.hasMounted) { + this.forceRenderChildren(); + } + } + previousRequest = assign({}, data); + return result; + }); + }; + }; - return refetchMethod(...args); + const createBoundFetchMore = (dataKey, fetchMoreMethod) => { + return (...args) => { + this.data[dataKey] = assign(this.data[dataKey], { + loading: true, + fetchMore, + }); + + this.hasQueryDataChanged = true; + + if (this.hasMounted) { + this.forceRenderChildren(); + } + + return fetchMoreMethod(...args); }; }; - let oldData = {}; const forceRender = ({ errors, data = oldData }: any) => { const resultKeyConflict: boolean = ( 'errors' in data || 'loading' in data || 'refetch' in data || 'startPolling' in data || - 'stopPolling' in data + 'stopPolling' in data || + 'fetchMore' in data ); invariant(!resultKeyConflict, `the result of the '${key}' query contains keys that ` + `conflict with the return object. 'errors', 'loading', ` + - `'startPolling', 'stopPolling', and 'refetch' cannot be ` + - `returned keys` + `'startPolling', 'stopPolling', 'refetch', and 'fetchMore' ` + + `cannot be returned keys` ); // only rerender child component if data has changed @@ -394,10 +429,10 @@ export default function connect(opts?: ConnectOptions) { this.data[key] = assign({ loading: false, - errors, refetch, // copy over refetch method startPolling, stopPolling, + fetchMore, }, data); if (this.hasMounted) { @@ -420,11 +455,14 @@ export default function connect(opts?: ConnectOptions) { (this.querySubscriptions[key] as any).startPolling; stopPolling = this.queryObservables[key].stopPolling || (this.querySubscriptions[key] as any).stopPolling; + fetchMore = createBoundFetchMore(key, + this.queryObservables[key].fetchMore); this.data[key] = assign(this.data[key], { refetch, startPolling, stopPolling, + fetchMore, }); } @@ -466,12 +504,14 @@ export default function connect(opts?: ConnectOptions) { const resultKeyConflict: boolean = ( 'errors' in data || 'loading' in data || + 'fetchMore' in data || 'refetch' in data ); invariant(!resultKeyConflict, `the result of the '${key}' mutation contains keys that ` + - `conflict with the return object. 'errors', 'loading', and 'refetch' cannot be ` + + `conflict with the return object. 'errors', 'loading', ` + + `fetchMore' and 'refetch' cannot be ` + `returned keys` ); diff --git a/test/client/connect/queries.tsx b/test/client/connect/queries.tsx index be1a9a4f24..9b231e95eb 100644 --- a/test/client/connect/queries.tsx +++ b/test/client/connect/queries.tsx @@ -1742,6 +1742,102 @@ describe('queries', () => { ); }); + it('should update props after fetching more with fetchMore', (done) => { + const store = createStore(() => ({ })); + + const query = gql` + query people($skip: Int) { + allPeople(first: 1, skip: $skip) { + people { + name + } + } + } + `; + + const variables = { skip: 0 }; + const variablesMore = { skip: 1 }; + + const data = { + allPeople: { + people: [ + { + name: 'Luke Skywalker', + }, + ], + }, + }; + + const dataMore = { + allPeople: { + people: [ + { + name: 'Anakin Skywalker', + }, + ], + }, + }; + + const networkInterface = mockNetworkInterface({ + request: { query, variables }, + result: { data }, + }, { + request: { query, variables: variablesMore }, + result: { data: dataMore }, + }); + + const client = new ApolloClient({ + networkInterface, + }); + + function mapQueriesToProps() { + return { + luke: { query, variables }, + }; + }; + + let iter = 0; + + @connect({ mapQueriesToProps }) + class Container extends React.Component { + componentDidUpdate(prevProps) { + if (iter === 0) { + expect(prevProps.luke.loading).to.be.true; + expect(this.props.luke.allPeople).to.deep.equal(data.allPeople); + this.props.luke.fetchMore({ + variables: variablesMore, + updateQuery: (prev, { fetchMoreResult }) => { + return { + allPeople: { + people: prev.allPeople.people.concat(fetchMoreResult.data.allPeople.people), + }, + }; + }, + }); + } else if (iter === 1) { + expect(prevProps.luke.loading).to.be.true; + expect(this.props.luke.allPeople).to.deep.equal(data.allPeople); + } else if (iter === 2) { + expect(this.props.luke.loading).to.be.false; + expect(this.props.luke.allPeople.people).to.deep.equal(data.allPeople.people.concat(dataMore.allPeople.people)); + done(); + } else { + throw new Error('should not reach this statement'); + } + iter++; + } + render() { + return ; + } + }; + + mount( + + + + ); + }); + it('should prefill any data already in the store', (done) => { const query = gql` diff --git a/test/server/index.tsx b/test/server/index.tsx index c009976f76..81b0b37bd9 100644 --- a/test/server/index.tsx +++ b/test/server/index.tsx @@ -23,16 +23,36 @@ describe('SSR', () => { return
{data.loading ? 'loading' : 'loaded'}
; }; + const query = gql` + query App { + currentUser { + firstName + } + } + `; + + const data1 = { + currentUser: { + firstName: 'James', + }, + }; + + const networkInterface = mockNetworkInterface( + { + request: { query }, + result: { data: data1 }, + delay: 50, + } + ); + + const client = new ApolloClient({ + networkInterface, + }); + const WrappedElement = connect({ mapQueriesToProps: () => ({ data: { - query: gql` - query Feed { - currentUser { - login - } - } - `, + query, }, }), })(Element); @@ -135,7 +155,7 @@ describe('SSR', () => { getDataFromTree(app) .then(({ initialState }) => { expect(initialState.apollo.data).to.exist; - expect(initialState.apollo.data['ROOT_QUERY.currentUser']).to.exist; + expect(initialState.apollo.data['$ROOT_QUERY.currentUser']).to.exist; done(); }); }); @@ -183,7 +203,7 @@ describe('SSR', () => { getDataFromTree(app) .then(({ initialState }) => { expect(initialState.apollo.data).to.exist; - expect(initialState.apollo.data['ROOT_QUERY.currentUser({"ctrn":1})']).to.exist; + expect(initialState.apollo.data['$ROOT_QUERY.currentUser({"ctrn":1})']).to.exist; done(); }) .catch(done); @@ -233,7 +253,7 @@ describe('SSR', () => { getDataFromTree(app) .then(({ initialState }) => { expect(initialState.apollo.data).to.exist; - expect(initialState.apollo.data['ROOT_QUERY.currentUser({"ctrn":0})']).to.exist; + expect(initialState.apollo.data['$ROOT_QUERY.currentUser({"ctrn":0})']).to.exist; done(); }) .catch(done); @@ -284,7 +304,7 @@ describe('SSR', () => { getDataFromTree(app) .then(({ initialState }) => { expect(initialState.apollo.data).to.exist; - expect(initialState.apollo.data['ROOT_QUERY.currentUser']).to.not.exist; + expect(initialState.apollo.data['$ROOT_QUERY.currentUser']).to.not.exist; done(); }); }); diff --git a/typings/globals/enzyme/index.d.ts b/typings/globals/enzyme/index.d.ts index ed31de645b..062da08aae 100644 --- a/typings/globals/enzyme/index.d.ts +++ b/typings/globals/enzyme/index.d.ts @@ -403,4 +403,4 @@ declare module "enzyme" { export function describeWithDOM(description: String, fn: Function): void; export function spyLifecycle(component: typeof Component): void; -} \ No newline at end of file +} diff --git a/typings/globals/es6-promise/index.d.ts b/typings/globals/es6-promise/index.d.ts index c69a4b841e..7dfe805008 100644 --- a/typings/globals/es6-promise/index.d.ts +++ b/typings/globals/es6-promise/index.d.ts @@ -69,4 +69,4 @@ declare module 'es6-promise' { export var Promise: typeof foo; } export = rsvp; -} \ No newline at end of file +} diff --git a/typings/globals/graphql/index.d.ts b/typings/globals/graphql/index.d.ts index d700eacb70..80167266f0 100644 --- a/typings/globals/graphql/index.d.ts +++ b/typings/globals/graphql/index.d.ts @@ -936,4 +936,4 @@ declare module "graphql" { directives?: Array; } -} \ No newline at end of file +} diff --git a/typings/globals/isomorphic-fetch/index.d.ts b/typings/globals/isomorphic-fetch/index.d.ts index a3767cb7f0..0fa7c7d175 100644 --- a/typings/globals/isomorphic-fetch/index.d.ts +++ b/typings/globals/isomorphic-fetch/index.d.ts @@ -113,4 +113,4 @@ declare module "isomorphic-fetch" { export default IFetchStatic; } -declare var fetch: IFetchStatic; \ No newline at end of file +declare var fetch: IFetchStatic; diff --git a/typings/globals/mocha/index.d.ts b/typings/globals/mocha/index.d.ts index 852e7b6ac8..75b3492494 100644 --- a/typings/globals/mocha/index.d.ts +++ b/typings/globals/mocha/index.d.ts @@ -230,4 +230,4 @@ declare namespace Mocha { declare module "mocha" { export = Mocha; -} \ No newline at end of file +} diff --git a/typings/globals/node/index.d.ts b/typings/globals/node/index.d.ts index ccbe6facd0..56569c5ef9 100644 --- a/typings/globals/node/index.d.ts +++ b/typings/globals/node/index.d.ts @@ -2298,4 +2298,4 @@ declare module "constants" { export var W_OK: number; export var X_OK: number; export var UV_UDP_REUSEADDR: number; -} \ No newline at end of file +} diff --git a/typings/globals/react-addons-test-utils/index.d.ts b/typings/globals/react-addons-test-utils/index.d.ts index e1db7bc49e..6b88350e68 100644 --- a/typings/globals/react-addons-test-utils/index.d.ts +++ b/typings/globals/react-addons-test-utils/index.d.ts @@ -149,4 +149,4 @@ declare namespace __React { declare module "react-addons-test-utils" { import TestUtils = __React.__Addons.TestUtils; export = TestUtils; -} \ No newline at end of file +} diff --git a/typings/globals/react-dom/index.d.ts b/typings/globals/react-dom/index.d.ts index a1973bc185..33b5150f9b 100644 --- a/typings/globals/react-dom/index.d.ts +++ b/typings/globals/react-dom/index.d.ts @@ -67,4 +67,4 @@ declare module "react-dom" { declare module "react-dom/server" { import DOMServer = __React.__DOMServer; export = DOMServer; -} \ No newline at end of file +} diff --git a/typings/globals/react/index.d.ts b/typings/globals/react/index.d.ts index 86eabfc9af..36e36a87f7 100644 --- a/typings/globals/react/index.d.ts +++ b/typings/globals/react/index.d.ts @@ -2463,4 +2463,4 @@ declare namespace JSX { text: React.SVGProps; tspan: React.SVGProps; } -} \ No newline at end of file +} diff --git a/typings/modules/apollo-client/index.d.ts b/typings/modules/apollo-client/index.d.ts index 6b5f1b4411..1b8a196628 100644 --- a/typings/modules/apollo-client/index.d.ts +++ b/typings/modules/apollo-client/index.d.ts @@ -14,12 +14,28 @@ declare module 'apollo-client/middleware' { export * from '~apollo-client/middleware'; } +// Generated by typings +// Source: node_modules/apollo-client/afterware.d.ts +declare module '~apollo-client/afterware' { +export interface AfterwareResponse { + response: IResponse; + options: RequestInit; +} +export interface AfterwareInterface { + applyAfterware(response: AfterwareResponse, next: Function): any; +} +} +declare module 'apollo-client/afterware' { +export * from '~apollo-client/afterware'; +} + // Generated by typings // Source: node_modules/apollo-client/networkInterface.d.ts declare module '~apollo-client/networkInterface' { import 'whatwg-fetch'; import { GraphQLResult, Document } from 'graphql'; import { MiddlewareInterface } from '~apollo-client/middleware'; +import { AfterwareInterface } from '~apollo-client/afterware'; export interface Request { debugName?: string; query?: Document; @@ -43,12 +59,18 @@ export interface HTTPNetworkInterface extends BatchedNetworkInterface { _uri: string; _opts: RequestInit; _middlewares: MiddlewareInterface[]; + _afterwares: AfterwareInterface[]; use(middlewares: MiddlewareInterface[]): any; + useAfter(afterwares: AfterwareInterface[]): any; } export interface RequestAndOptions { request: Request; options: RequestInit; } +export interface ResponseAndOptions { + response: IResponse; + options: RequestInit; +} export function addQueryMerging(networkInterface: NetworkInterface): BatchedNetworkInterface; export function printRequest(request: Request): PrintedRequest; export function createNetworkInterface(uri: string, opts?: RequestInit): HTTPNetworkInterface; @@ -71,7 +93,18 @@ export interface StoreObject { __typename?: string; [storeFieldKey: string]: StoreValue; } -export type StoreValue = number | string | string[]; +export interface IdValue { + type: "id"; + id: string; + generated: boolean; +} +export interface JsonValue { + type: "json"; + json: any; +} +export type StoreValue = number | string | string[] | IdValue | JsonValue; +export function isIdValue(idObject: StoreValue): idObject is IdValue; +export function isJsonValue(jsonObject: StoreValue): jsonObject is JsonValue; export function data(previousState: NormalizedCache, action: ApolloAction, queries: QueryStore, mutations: MutationStore, config: ApolloReducerConfig): NormalizedCache; } declare module 'apollo-client/data/store' { @@ -140,10 +173,24 @@ declare module 'apollo-client/mutations/store' { export * from '~apollo-client/mutations/store'; } +// Generated by typings +// Source: node_modules/apollo-client/optimistic-data/store.d.ts +declare module '~apollo-client/optimistic-data/store' { +import { NormalizedCache } from '~apollo-client/data/store'; +export type OptimisticStore = { + mutationId: string; + data: NormalizedCache; +}[]; +export function optimistic(previousState: any[], action: any, store: any, config: any): OptimisticStore; +} +declare module 'apollo-client/optimistic-data/store' { +export * from '~apollo-client/optimistic-data/store'; +} + // Generated by typings // Source: node_modules/apollo-client/actions.d.ts declare module '~apollo-client/actions' { -import { GraphQLResult } from 'graphql'; +import { GraphQLResult, SelectionSet, FragmentDefinition } from 'graphql'; import { SelectionSetWithRoot } from '~apollo-client/queries/store'; import { MutationBehavior } from '~apollo-client/data/mutationResults'; import { FragmentMap } from '~apollo-client/queries/getFromAST'; @@ -194,6 +241,8 @@ export interface MutationInitAction { variables: Object; mutationId: string; fragmentMap: FragmentMap; + optimisticResponse: Object; + resultBehaviors?: MutationBehavior[]; } export function isMutationInitAction(action: ApolloAction): action is MutationInitAction; export interface MutationResultAction { @@ -207,15 +256,22 @@ export interface MutationErrorAction { type: 'APOLLO_MUTATION_ERROR'; error: Error; mutationId: string; - resultBehaviors?: MutationBehavior[]; } export function isMutationErrorAction(action: ApolloAction): action is MutationErrorAction; +export interface UpdateQueryResultAction { + type: 'APOLLO_UPDATE_QUERY_RESULT'; + queryVariables: any; + querySelectionSet: SelectionSet; + queryFragments: FragmentDefinition[]; + newResult: Object; +} +export function isUpdateQueryResultAction(action: ApolloAction): action is UpdateQueryResultAction; export interface StoreResetAction { type: 'APOLLO_STORE_RESET'; observableQueryIds: string[]; } export function isStoreResetAction(action: ApolloAction): action is StoreResetAction; -export type ApolloAction = QueryResultAction | QueryErrorAction | QueryInitAction | QueryResultClientAction | QueryStopAction | MutationInitAction | MutationResultAction | MutationErrorAction | StoreResetAction; +export type ApolloAction = QueryResultAction | QueryErrorAction | QueryInitAction | QueryResultClientAction | QueryStopAction | MutationInitAction | MutationResultAction | MutationErrorAction | UpdateQueryResultAction | StoreResetAction; } declare module 'apollo-client/actions' { export * from '~apollo-client/actions'; @@ -227,6 +283,7 @@ declare module '~apollo-client/store' { import { NormalizedCache } from '~apollo-client/data/store'; import { QueryStore } from '~apollo-client/queries/store'; import { MutationStore } from '~apollo-client/mutations/store'; +import { OptimisticStore } from '~apollo-client/optimistic-data/store'; import { ApolloAction } from '~apollo-client/actions'; import { IdGetter } from '~apollo-client/data/extensions'; import { MutationBehaviorReducerMap } from '~apollo-client/data/mutationResults'; @@ -234,6 +291,7 @@ export interface Store { data: NormalizedCache; queries: QueryStore; mutations: MutationStore; + optimistic: OptimisticStore; } export interface ApolloStore { dispatch: (action: ApolloAction) => void; @@ -250,34 +308,12 @@ export interface ApolloReducerConfig { dataIdFromObject?: IdGetter; mutationBehaviorReducers?: MutationBehaviorReducerMap; } +export function getDataWithOptimisticResults(store: Store): NormalizedCache; } declare module 'apollo-client/store' { export * from '~apollo-client/store'; } -// Generated by typings -// Source: node_modules/apollo-client/util/Observable.d.ts -declare module '~apollo-client/util/Observable' { -export type CleanupFunction = () => void; -export type SubscriberFunction = (observer: Observer) => (Subscription | CleanupFunction); -export class Observable { - private subscriberFunction; - constructor(subscriberFunction: SubscriberFunction); - subscribe(observer: Observer): Subscription; -} -export interface Observer { - next?: (value: T) => void; - error?: (error: Error) => void; - complete?: () => void; -} -export interface Subscription { - unsubscribe: CleanupFunction; -} -} -declare module 'apollo-client/util/Observable' { -export * from '~apollo-client/util/Observable'; -} - // Generated by typings // Source: node_modules/apollo-client/QueryManager.d.ts declare module '~apollo-client/QueryManager' { @@ -285,68 +321,56 @@ import { NetworkInterface } from '~apollo-client/networkInterface'; import { ApolloStore, Store } from '~apollo-client/store'; import { QueryStoreValue } from '~apollo-client/queries/store'; import { QueryTransformer } from '~apollo-client/queries/queryTransform'; -import { Document, FragmentDefinition } from 'graphql'; -import { MutationBehavior } from '~apollo-client/data/mutationResults'; +import { NormalizedCache } from '~apollo-client/data/store'; +import { Document, FragmentDefinition, SelectionSet } from 'graphql'; +import { MutationBehavior, MutationQueryReducersMap } from '~apollo-client/data/mutationResults'; +import { QueryScheduler } from '~apollo-client/scheduler'; import { ApolloQueryResult } from '~apollo-client/index'; -import { Observable, Observer, Subscription, SubscriberFunction } from '~apollo-client/util/Observable'; -export class ObservableQuery extends Observable { - refetch: (variables?: any) => Promise; - stopPolling: () => void; - startPolling: (p: number) => void; - constructor(options: { - subscriberFunction: SubscriberFunction; - refetch: (variables?: any) => Promise; - stopPolling: () => void; - startPolling: (p: number) => void; - }); - subscribe(observer: Observer): Subscription; - result(): Promise; -} -export interface WatchQueryOptions { - query: Document; - variables?: { - [key: string]: any; - }; - forceFetch?: boolean; - returnPartialData?: boolean; - pollInterval?: number; - fragments?: FragmentDefinition[]; -} +import { Observer, Subscription } from '~apollo-client/util/Observable'; +import { WatchQueryOptions } from '~apollo-client/watchQueryOptions'; +import { ObservableQuery } from '~apollo-client/ObservableQuery'; export type QueryListener = (queryStoreValue: QueryStoreValue) => void; export class QueryManager { + pollingTimers: { + [queryId: string]: NodeJS.Timer | any; + }; + scheduler: QueryScheduler; + store: ApolloStore; private networkInterface; - private store; private reduxRootKey; - private pollingTimers; private queryTransformer; private queryListeners; + private queryResults; private idCounter; - private scheduler; private batcher; - private batcherPollInterval; + private batchInterval; private fetchQueryPromises; private observableQueries; - constructor({networkInterface, store, reduxRootKey, queryTransformer, shouldBatch}: { + constructor({networkInterface, store, reduxRootKey, queryTransformer, shouldBatch, batchInterval}: { networkInterface: NetworkInterface; store: ApolloStore; reduxRootKey: string; queryTransformer?: QueryTransformer; shouldBatch?: Boolean; + batchInterval?: number; }); broadcastNewStore(store: any): void; - mutate({mutation, variables, resultBehaviors, fragments}: { + mutate({mutation, variables, resultBehaviors, fragments, optimisticResponse, updateQueries}: { mutation: Document; variables?: Object; resultBehaviors?: MutationBehavior[]; fragments?: FragmentDefinition[]; + optimisticResponse?: Object; + updateQueries?: MutationQueryReducersMap; }): Promise; - queryListenerForObserver(options: WatchQueryOptions, observer: Observer): QueryListener; + queryListenerForObserver(queryId: string, options: WatchQueryOptions, observer: Observer): QueryListener; watchQuery(options: WatchQueryOptions, shouldSubscribe?: boolean): ObservableQuery; query(options: WatchQueryOptions): Promise; fetchQuery(queryId: string, options: WatchQueryOptions): Promise; generateQueryId(): string; stopQueryInStore(queryId: string): void; getApolloState(): Store; + getDataWithOptimisticResults(): NormalizedCache; addQueryListener(queryId: string, listener: QueryListener): void; removeQueryListener(queryId: string): void; addFetchQueryPromise(requestId: number, promise: Promise, resolve: (result: ApolloQueryResult) => void, reject: (error: Error) => void): void; @@ -355,9 +379,19 @@ export class QueryManager { addQuerySubscription(queryId: string, querySubscription: Subscription): void; removeObservableQuery(queryId: string): void; resetStore(): void; + startQuery(queryId: string, options: WatchQueryOptions, listener: QueryListener): string; + stopQuery(queryId: string): void; + getQueryWithPreviousResult(queryId: string, isOptimistic?: boolean): { + previousResult: Object; + queryVariables: { + [key: string]: any; + }; + querySelectionSet: SelectionSet; + queryFragments: FragmentDefinition[]; + }; + private collectResultBehaviorsFromUpdateQueries(updateQueries, mutationResult, isOptimistic?); private fetchQueryOverInterface(queryId, options, network); - private startQuery(queryId, options, listener); - private stopQuery(queryId); + private isDifferentResult(queryId, result); private broadcastQueries(); private generateRequestId(); } @@ -366,6 +400,125 @@ declare module 'apollo-client/QueryManager' { export * from '~apollo-client/QueryManager'; } +// Generated by typings +// Source: node_modules/apollo-client/util/Observable.d.ts +declare module '~apollo-client/util/Observable' { +export type CleanupFunction = () => void; +export type SubscriberFunction = (observer: Observer) => (Subscription | CleanupFunction); +export class Observable { + private subscriberFunction; + constructor(subscriberFunction: SubscriberFunction); + subscribe(observer: Observer): Subscription; +} +export interface Observer { + next?: (value: T) => void; + error?: (error: Error) => void; + complete?: () => void; +} +export interface Subscription { + unsubscribe: CleanupFunction; +} +} +declare module 'apollo-client/util/Observable' { +export * from '~apollo-client/util/Observable'; +} + +// Generated by typings +// Source: node_modules/apollo-client/scheduler.d.ts +declare module '~apollo-client/scheduler' { +import { QueryManager, QueryListener } from '~apollo-client/QueryManager'; +import { ObservableQuery } from '~apollo-client/ObservableQuery'; +import { WatchQueryOptions } from '~apollo-client/watchQueryOptions'; +export class QueryScheduler { + inFlightQueries: { + [queryId: string]: WatchQueryOptions; + }; + registeredQueries: { + [queryId: string]: WatchQueryOptions; + }; + intervalQueries: { + [interval: number]: string[]; + }; + queryManager: QueryManager; + private pollingTimers; + constructor({queryManager}: { + queryManager: QueryManager; + }); + checkInFlight(queryId: string): boolean; + fetchQuery(queryId: string, options: WatchQueryOptions): Promise<{}>; + startPollingQuery(options: WatchQueryOptions, queryId?: string, firstFetch?: boolean, listener?: QueryListener): string; + stopPollingQuery(queryId: string): void; + fetchQueriesOnInterval(interval: number): void; + addQueryOnInterval(queryId: string, queryOptions: WatchQueryOptions): void; + registerPollingQuery(queryOptions: WatchQueryOptions): ObservableQuery; + private addInFlight(queryId, options); + private removeInFlight(queryId); +} +} +declare module 'apollo-client/scheduler' { +export * from '~apollo-client/scheduler'; +} + +// Generated by typings +// Source: node_modules/apollo-client/ObservableQuery.d.ts +declare module '~apollo-client/ObservableQuery' { +import { WatchQueryOptions, FetchMoreQueryOptions } from '~apollo-client/watchQueryOptions'; +import { Observable } from '~apollo-client/util/Observable'; +import { QueryScheduler } from '~apollo-client/scheduler'; +import { ApolloQueryResult } from '~apollo-client/index'; +export interface FetchMoreOptions { + updateQuery: (previousQueryResult: Object, options: { + fetchMoreResult: Object; + queryVariables: Object; + }) => Object; +} +export class ObservableQuery extends Observable { + refetch: (variables?: any) => Promise; + fetchMore: (options: FetchMoreQueryOptions & FetchMoreOptions) => Promise; + stopPolling: () => void; + startPolling: (p: number) => void; + options: WatchQueryOptions; + private queryId; + private scheduler; + private queryManager; + constructor({scheduler, options, shouldSubscribe}: { + scheduler: QueryScheduler; + options: WatchQueryOptions; + shouldSubscribe?: boolean; + }); + result(): Promise; +} +} +declare module 'apollo-client/ObservableQuery' { +export * from '~apollo-client/ObservableQuery'; +} + +// Generated by typings +// Source: node_modules/apollo-client/watchQueryOptions.d.ts +declare module '~apollo-client/watchQueryOptions' { +import { Document, FragmentDefinition } from 'graphql'; +export interface WatchQueryOptions { + query: Document; + variables?: { + [key: string]: any; + }; + forceFetch?: boolean; + returnPartialData?: boolean; + noFetch?: boolean; + pollInterval?: number; + fragments?: FragmentDefinition[]; +} +export interface FetchMoreQueryOptions { + query?: Document; + variables?: { + [key: string]: any; + }; +} +} +declare module 'apollo-client/watchQueryOptions' { +export * from '~apollo-client/watchQueryOptions'; +} + // Generated by typings // Source: node_modules/apollo-client/queries/getFromAST.d.ts declare module '~apollo-client/queries/getFromAST' { @@ -376,11 +529,10 @@ export function getOperationName(doc: Document): string; export function getFragmentDefinitions(doc: Document): FragmentDefinition[]; export function getQueryDefinition(doc: Document): OperationDefinition; export function getFragmentDefinition(doc: Document): FragmentDefinition; -export function replaceOperationDefinition(doc: Document, newOpDef: OperationDefinition): Document; export interface FragmentMap { [fragmentName: string]: FragmentDefinition; } -export function createFragmentMap(fragments: FragmentDefinition[]): FragmentMap; +export function createFragmentMap(fragments?: FragmentDefinition[]): FragmentMap; export function addFragmentsToDocument(queryDoc: Document, fragments: FragmentDefinition[]): Document; } declare module 'apollo-client/queries/getFromAST' { @@ -393,11 +545,12 @@ declare module '~apollo-client/data/readFromStore' { import { SelectionSet, Document } from 'graphql'; import { FragmentMap } from '~apollo-client/queries/getFromAST'; import { NormalizedCache } from '~apollo-client/data/store'; -export function readQueryFromStore({store, query, variables, returnPartialData}: { +export function readQueryFromStore({store, query, variables, returnPartialData, fragmentMap}: { store: NormalizedCache; query: Document; variables?: Object; returnPartialData?: boolean; + fragmentMap?: FragmentMap; }): Object; export function readFragmentFromStore({store, fragment, rootId, variables, returnPartialData}: { store: NormalizedCache; @@ -433,12 +586,13 @@ export function writeFragmentToStore({result, fragment, store, variables, dataId variables?: Object; dataIdFromObject?: IdGetter; }): NormalizedCache; -export function writeQueryToStore({result, query, store, variables, dataIdFromObject}: { +export function writeQueryToStore({result, query, store, variables, dataIdFromObject, fragmentMap}: { result: Object; query: Document; store?: NormalizedCache; variables?: Object; dataIdFromObject?: IdGetter; + fragmentMap?: FragmentMap; }): NormalizedCache; export function writeSelectionSetToStore({result, dataId, selectionSet, store, variables, dataIdFromObject, fragmentMap}: { dataId: string; @@ -469,12 +623,11 @@ export * from '~apollo-client/data/extensions'; // Generated by typings // Source: node_modules/apollo-client/queries/queryTransform.d.ts declare module '~apollo-client/queries/queryTransform' { -import { SelectionSet, OperationDefinition } from 'graphql'; +import { Document, SelectionSet } from 'graphql'; export type QueryTransformer = (selectionSet: SelectionSet) => void; -export function addFieldToSelectionSet(fieldName: string, selectionSet: SelectionSet): SelectionSet; -export function addTypenameToSelectionSet(selectionSet: SelectionSet): SelectionSet; -export function addTypenameToQuery(queryDef: OperationDefinition): OperationDefinition; -export function applyTransformerToOperation(queryDef: OperationDefinition, queryTransformer: QueryTransformer): OperationDefinition; +export function addFieldToSelectionSet(fieldName: string, selectionSet: SelectionSet): void; +export function addTypenameToSelectionSet(selectionSet: SelectionSet): void; +export function applyTransformers(doc: Document, queryTransformers: QueryTransformer[]): Document; } declare module 'apollo-client/queries/queryTransform' { export * from '~apollo-client/queries/queryTransform'; @@ -504,11 +657,11 @@ export * from '~apollo-client/data/scopeQuery'; // Source: node_modules/apollo-client/data/mutationResults.d.ts declare module '~apollo-client/data/mutationResults' { import { NormalizedCache } from '~apollo-client/data/store'; -import { GraphQLResult, SelectionSet } from 'graphql'; +import { GraphQLResult, SelectionSet, FragmentDefinition } from 'graphql'; import { FragmentMap } from '~apollo-client/queries/getFromAST'; import { StorePath } from '~apollo-client/data/scopeQuery'; import { ApolloReducerConfig } from '~apollo-client/store'; -export type MutationBehavior = MutationArrayInsertBehavior | MutationArrayDeleteBehavior | MutationDeleteBehavior; +export type MutationBehavior = MutationArrayInsertBehavior | MutationArrayDeleteBehavior | MutationDeleteBehavior | MutationQueryResultBehavior; export type MutationArrayInsertBehavior = { type: 'ARRAY_INSERT'; resultPath: StorePath; @@ -524,6 +677,13 @@ export type MutationArrayDeleteBehavior = { storePath: StorePath; dataId: string; }; +export type MutationQueryResultBehavior = { + type: 'QUERY_RESULT'; + queryVariables: any; + querySelectionSet: SelectionSet; + queryFragments: FragmentDefinition[]; + newResult: Object; +}; export type ArrayInsertWhere = 'PREPEND' | 'APPEND'; export type MutationBehaviorReducerArgs = { behavior: MutationBehavior; @@ -538,6 +698,15 @@ export type MutationBehaviorReducerMap = { }; export type MutationBehaviorReducer = (state: NormalizedCache, args: MutationBehaviorReducerArgs) => NormalizedCache; export function cleanArray(originalArray: any, dataId: any): any; +export function mutationResultQueryResultReducer(state: NormalizedCache, {behavior, config}: MutationBehaviorReducerArgs): NormalizedCache; +export type MutationQueryReducer = (previousResult: Object, options: { + mutationResult: Object; + queryName: Object; + queryVariables: Object; +}) => Object; +export type MutationQueryReducersMap = { + [queryName: string]: MutationQueryReducer; +}; export const defaultMutationBehaviorReducers: { [type: string]: MutationBehaviorReducer; }; @@ -550,10 +719,12 @@ export * from '~apollo-client/data/mutationResults'; // Source: node_modules/apollo-client/index.d.ts declare module '~apollo-client/index' { import { NetworkInterface, createNetworkInterface, addQueryMerging } from '~apollo-client/networkInterface'; -import { Document, FragmentDefinition } from 'graphql'; +import { Document, FragmentDefinition, SelectionSet } from 'graphql'; import { print } from 'graphql-tag/printer'; import { createApolloStore, ApolloStore, createApolloReducer, ApolloReducerConfig } from '~apollo-client/store'; -import { QueryManager, WatchQueryOptions, ObservableQuery } from '~apollo-client/QueryManager'; +import { QueryManager } from '~apollo-client/QueryManager'; +import { ObservableQuery } from '~apollo-client/ObservableQuery'; +import { WatchQueryOptions } from '~apollo-client/watchQueryOptions'; import { readQueryFromStore, readFragmentFromStore } from '~apollo-client/data/readFromStore'; import { writeQueryToStore, writeFragmentToStore } from '~apollo-client/data/writeToStore'; import { IdGetter } from '~apollo-client/data/extensions'; @@ -566,7 +737,7 @@ export type ApolloQueryResult = { export let fragmentDefinitionsMap: { [fragmentName: string]: FragmentDefinition[]; }; -export function createFragment(doc: Document, fragments?: FragmentDefinition[]): FragmentDefinition[]; +export function createFragment(doc: Document, fragments?: (FragmentDefinition[] | FragmentDefinition[][])): FragmentDefinition[]; export function disableFragmentWarnings(): void; export function enableFragmentWarnings(): void; export function clearFragmentDefinitions(): void; @@ -582,7 +753,8 @@ export default class ApolloClient { shouldForceFetch: boolean; dataId: IdGetter; fieldWithArgs: (fieldName: string, args?: Object) => string; - constructor({networkInterface, reduxRootKey, initialState, dataIdFromObject, queryTransformer, shouldBatch, ssrMode, ssrForceFetchDelay, mutationBehaviorReducers}?: { + batchInterval: number; + constructor({networkInterface, reduxRootKey, initialState, dataIdFromObject, queryTransformer, shouldBatch, ssrMode, ssrForceFetchDelay, mutationBehaviorReducers, batchInterval}?: { networkInterface?: NetworkInterface; reduxRootKey?: string; initialState?: any; @@ -592,6 +764,7 @@ export default class ApolloClient { ssrMode?: boolean; ssrForceFetchDelay?: number; mutationBehaviorReducers?: MutationBehaviorReducerMap; + batchInterval?: number; }); watchQuery: (options: WatchQueryOptions) => ObservableQuery; query: (options: WatchQueryOptions) => Promise<{ @@ -599,6 +772,7 @@ export default class ApolloClient { }>; mutate: (options: { mutation: Document; + variables?: Object; resultBehaviors?: ({ type: "ARRAY_INSERT"; resultPath: (string | number)[]; @@ -611,9 +785,22 @@ export default class ApolloClient { } | { type: "DELETE"; dataId: string; + } | { + type: "QUERY_RESULT"; + queryVariables: any; + querySelectionSet: SelectionSet; + queryFragments: FragmentDefinition[]; + newResult: Object; })[]; - variables?: Object; fragments?: FragmentDefinition[]; + optimisticResponse?: Object; + updateQueries?: { + [queryName: string]: (previousResult: Object, options: { + mutationResult: Object; + queryName: Object; + queryVariables: Object; + }) => Object; + }; }) => Promise<{ data: any; }>; diff --git a/typings/modules/apollo-client/typings.json b/typings/modules/apollo-client/typings.json index 1a0fb6525e..e5eb667782 100644 --- a/typings/modules/apollo-client/typings.json +++ b/typings/modules/apollo-client/typings.json @@ -1,8 +1,8 @@ { "resolution": "main", "tree": { - "src": "/Users/sashko/git/react-apollo/node_modules/apollo-client/index.d.ts", + "src": "/Users/slava/work/react-apollo/node_modules/apollo-client/index.d.ts", "raw": "file:node_modules/apollo-client/index.d.ts", - "typings": "/Users/sashko/git/react-apollo/node_modules/apollo-client/index.d.ts" + "typings": "/Users/slava/work/react-apollo/node_modules/apollo-client/index.d.ts" } } diff --git a/typings/modules/chai-enzyme/index.d.ts b/typings/modules/chai-enzyme/index.d.ts index 532fa9c663..6361aa4ed9 100644 --- a/typings/modules/chai-enzyme/index.d.ts +++ b/typings/modules/chai-enzyme/index.d.ts @@ -1,6 +1,6 @@ // Generated by typings // Source: https://raw.githubusercontent.com/asvetliakov/typings-enzyme/master/index.d.ts -declare module '~chai-enzyme~enzyme/index' { +declare module '~chai-enzyme~enzyme' { /** * This is copied from: * https://github.com/DefinitelyTyped/DefinitelyTyped/blob/732748384322640244af4e61414de427e2f55e1e/enzyme/enzyme.d.ts @@ -350,14 +350,10 @@ namespace Enzyme { export = Enzyme; } -declare module '~chai-enzyme~enzyme' { -import main = require('~chai-enzyme~enzyme/index'); -export = main; -} // Generated by typings // Source: https://raw.githubusercontent.com/asvetliakov/typings-chai-enzyme/db5cc74f6d9496e0e1e23c87c6cb11aee8be64b6/index.d.ts -declare module '~chai-enzyme/index' { +declare module 'chai-enzyme' { import {EnzymeSelector, ShallowWrapper, ReactWrapper, CheerioWrapper} from '~chai-enzyme~enzyme'; import {Assertion} from '~chai/lib/Assertion'; @@ -499,11 +495,3 @@ function chaiEnzyMe(wrapper?: (debugWrapper: DebugWrapper) => string): (chai: an export = chaiEnzyMe; } -declare module 'chai-enzyme/index' { -import main = require('~chai-enzyme/index'); -export = main; -} -declare module 'chai-enzyme' { -import main = require('~chai-enzyme/index'); -export = main; -} diff --git a/typings/modules/chai/index.d.ts b/typings/modules/chai/index.d.ts index 0aba598308..34f998422a 100644 --- a/typings/modules/chai/index.d.ts +++ b/typings/modules/chai/index.d.ts @@ -1,6 +1,6 @@ // Generated by typings // Source: https://raw.githubusercontent.com/typed-typings/npm-assertion-error/105841317bd2bdd5d110bfb763e49e482a77230d/main.d.ts -declare module '~chai~assertion-error/main' { +declare module '~chai~assertion-error' { // Type definitions for assertion-error 1.0.0 // Project: https://github.com/chaijs/assertion-error // Definitions by: Bart van der Schoor @@ -22,9 +22,6 @@ export class AssertionError implements Error { public toJSON(stack: boolean): Object; } } -declare module '~chai~assertion-error' { -export * from '~chai~assertion-error/main'; -} // Generated by typings // Source: https://raw.githubusercontent.com/typed-typings/npm-chai/0b70226aa4ea9c3b37fe1c709db0423f11ed30d8/lib/Assert.d.ts @@ -508,7 +505,7 @@ export = main; // Generated by typings // Source: https://raw.githubusercontent.com/typed-typings/npm-chai/0b70226aa4ea9c3b37fe1c709db0423f11ed30d8/index.d.ts -declare module '~chai/index' { +declare module 'chai' { // Type definitions for chai 3.4.0 // Project: http://chaijs.com/ // Original Definitions by: Jed Mao , @@ -521,11 +518,3 @@ import chai = require('~chai/lib/Chai'); export = chai; } -declare module 'chai/index' { -import main = require('~chai/index'); -export = main; -} -declare module 'chai' { -import main = require('~chai/index'); -export = main; -} diff --git a/typings/modules/invariant/index.d.ts b/typings/modules/invariant/index.d.ts index 983011a491..5dbc378e83 100644 --- a/typings/modules/invariant/index.d.ts +++ b/typings/modules/invariant/index.d.ts @@ -1,15 +1,7 @@ // Generated by typings // Source: https://raw.githubusercontent.com/typed-typings/npm-invariant/58403cee078ebef52112c4227c4d23a97821ef5c/invariant.d.ts -declare module '~invariant/invariant' { +declare module 'invariant' { function invariant (condition: boolean, message: string, ...args: string[]): void; export = invariant; } -declare module 'invariant/invariant' { -import main = require('~invariant/invariant'); -export = main; -} -declare module 'invariant' { -import main = require('~invariant/invariant'); -export = main; -} diff --git a/typings/modules/lodash/index.d.ts b/typings/modules/lodash/index.d.ts index 6766bc036c..85e1a0c88e 100644 --- a/typings/modules/lodash/index.d.ts +++ b/typings/modules/lodash/index.d.ts @@ -1,6 +1,6 @@ // Generated by typings // Source: https://raw.githubusercontent.com/typed-typings/npm-lodash/73a6037f16173baee0b40136eee07b2c7ad243c8/index.d.ts -declare module '~lodash/index' { +declare module 'lodash' { /* ### 4.0.0 Changelog (https://github.com/lodash/lodash/wiki/Changelog) @@ -18283,11 +18283,3 @@ export = _; // Backward compatibility with --target es5 interface Map { } } -declare module 'lodash/index' { -import main = require('~lodash/index'); -export = main; -} -declare module 'lodash' { -import main = require('~lodash/index'); -export = main; -} diff --git a/typings/modules/object-assign/index.d.ts b/typings/modules/object-assign/index.d.ts index a79f0b2212..f27d4209be 100644 --- a/typings/modules/object-assign/index.d.ts +++ b/typings/modules/object-assign/index.d.ts @@ -1,6 +1,6 @@ // Generated by typings // Source: https://raw.githubusercontent.com/louy/typed-object-assign/91dd2548172cee5dc0aa531d1b452a76044dbff2/index.d.ts -declare module '~object-assign/index' { +declare module 'object-assign' { function assign(a: A): A; function assign(a: A, b: B): A & B; function assign(a: A, b: B, c: C): A & B & C; @@ -10,11 +10,3 @@ function assign(...a: A[]): A; export = assign; } -declare module 'object-assign/index' { -import main = require('~object-assign/index'); -export = main; -} -declare module 'object-assign' { -import main = require('~object-assign/index'); -export = main; -} diff --git a/typings/modules/react-redux/index.d.ts b/typings/modules/react-redux/index.d.ts index d5608590d1..d228022d15 100644 --- a/typings/modules/react-redux/index.d.ts +++ b/typings/modules/react-redux/index.d.ts @@ -1,6 +1,6 @@ // Generated by typings // Source: https://raw.githubusercontent.com/andrew-w-ross/typings-redux/master/redux.d.ts -declare module '~react-redux~redux/redux' { +declare module '~react-redux~redux' { module redux { //This should be extended export interface IAction { @@ -64,14 +64,10 @@ module redux { export = redux; } -declare module '~react-redux~redux' { -import main = require('~react-redux~redux/redux'); -export = main; -} // Generated by typings // Source: https://raw.githubusercontent.com/andrew-w-ross/typings-react-redux/6be88a57427f8ec220cad13d68565fd5f5711146/react-redux.d.ts -declare module '~react-redux/react-redux' { +declare module 'react-redux' { import * as React from 'react'; import {IStore, IDispatch} from '~react-redux~redux'; @@ -103,11 +99,3 @@ module ReactRedux { export = ReactRedux; } -declare module 'react-redux/react-redux' { -import main = require('~react-redux/react-redux'); -export = main; -} -declare module 'react-redux' { -import main = require('~react-redux/react-redux'); -export = main; -} diff --git a/typings/modules/sinon/index.d.ts b/typings/modules/sinon/index.d.ts index 360178fec8..3b42de8252 100644 --- a/typings/modules/sinon/index.d.ts +++ b/typings/modules/sinon/index.d.ts @@ -1,6 +1,6 @@ // Generated by typings // Source: https://raw.githubusercontent.com/typed-typings/npm-sinon/661f2077a206b669bcb5d2a568cc2e3c85818aec/lib/sinon.d.ts -declare module '~sinon/lib/sinon' { +declare module 'sinon' { // Type definitions for Sinon 1.16.0 // Project: http://sinonjs.org/ // Definitions by: William Sears @@ -435,11 +435,3 @@ var Sinon: Sinon.SinonStatic; export = Sinon; } -declare module 'sinon/lib/sinon' { -import main = require('~sinon/lib/sinon'); -export = main; -} -declare module 'sinon' { -import main = require('~sinon/lib/sinon'); -export = main; -}