diff --git a/Changelog.md b/Changelog.md index ed284e71e6..e11de1713c 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. +### vNext + +### v0.5.1 + +- Feature: Added link to [recompose](https://github.com/acdlite/recompose) to use the `compose` function. This makes it easy to combine multiple queries on a single component. [#194](https://github.com/apollostack/react-apollo/pull/194) ### v0.5.0 diff --git a/global.d.ts b/global.d.ts index 7cd4d5ebd2..e14539c351 100644 --- a/global.d.ts +++ b/global.d.ts @@ -11,6 +11,10 @@ declare module 'lodash.isequal' { export = main.isEqual; } +declare module 'recompose/compose' { + function hoc(component: any): any; + export default (...hocs) => hoc; +} declare module 'hoist-non-react-statics' { /** diff --git a/package.json b/package.json index c25733dea0..44c423d369 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "react-apollo", - "version": "0.5.0", + "version": "0.5.1", "description": "React data container for Apollo Client", "main": "index.js", "scripts": { @@ -101,6 +101,7 @@ "lodash.flatten": "^4.2.0", "lodash.isequal": "^4.1.1", "lodash.isobject": "^3.0.2", - "object-assign": "^4.0.1" + "object-assign": "^4.0.1", + "recompose": "^0.20.2" } } diff --git a/src/index.ts b/src/index.ts index b44fc4cb58..49fb025bd5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,7 @@ import ApolloProvider from './ApolloProvider'; import graphql, { withApollo } from './graphql'; -export { ApolloProvider, graphql, withApollo }; +// expose easy way to join queries from recompose +import compose from 'recompose/compose'; + +export { ApolloProvider, graphql, withApollo, compose }; diff --git a/test/react-web/client/graphql/shared-operations.tsx b/test/react-web/client/graphql/shared-operations.tsx index 16f9565368..5fab862721 100644 --- a/test/react-web/client/graphql/shared-operations.tsx +++ b/test/react-web/client/graphql/shared-operations.tsx @@ -20,6 +20,7 @@ import { } from '../../../mocks/components'; import graphql, { withApollo } from '../../../../src/graphql'; +import { compose } from '../../../../src/'; describe('shared opertations', () => { @@ -199,4 +200,39 @@ describe('shared opertations', () => { }, 25); }); + describe('compose', () => { + it('binds two queries to props with different syntax', () => { + const peopleQuery = gql`query people { allPeople(first: 1) { people { name } } }`; + const peopleData = { allPeople: { people: [ { name: 'Luke Skywalker' } ] } }; + + const shipsQuery = gql`query ships { allships(first: 1) { ships { name } } }`; + const shipsData = { allships: { ships: [ { name: 'Tie Fighter' } ] } }; + + + const networkInterface = mockNetworkInterface( + { request: { query: peopleQuery }, result: { data: peopleData } }, + { request: { query: shipsQuery }, result: { data: shipsData } } + ); + const client = new ApolloClient({ networkInterface }); + + const enhanced = compose( + graphql(peopleQuery, { name: 'people' }), + graphql(shipsQuery, { name: 'ships' }) + ); + + const ContainerWithData = enhanced((props) => { + const { people, ships } = props; + expect(people).to.exist; + expect(people.loading).to.be.true; + + expect(ships).to.exist; + expect(ships.loading).to.be.true; + return null; + }); + + const wrapper = mount(); + (wrapper as any).unmount(); + }); + }); + }); diff --git a/typings/modules/apollo-client/index.d.ts b/typings/modules/apollo-client/index.d.ts index 3af48d29e3..632c9d8c4e 100644 --- a/typings/modules/apollo-client/index.d.ts +++ b/typings/modules/apollo-client/index.d.ts @@ -52,6 +52,10 @@ export interface NetworkInterface { [others: string]: any; query(request: Request): Promise; } +export interface SubscriptionNetworkInterface extends NetworkInterface { + subscribe(request: Request, handler: (error, result) => void): number; + unsubscribe(id: Number): void; +} export interface BatchedNetworkInterface extends NetworkInterface { batchQuery(requests: Request[]): Promise; } @@ -389,6 +393,14 @@ 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 interface SubscriptionOptions { + query: Document; + variables?: { + [key: string]: any; + }; + fragments?: FragmentDefinition[]; + handler: (error: Object, result: Object) => void; +} export class QueryManager { pollingTimers: { [queryId: string]: NodeJS.Timer | any; @@ -441,6 +453,7 @@ export class QueryManager { removeObservableQuery(queryId: string): void; resetStore(): void; startQuery(queryId: string, options: WatchQueryOptions, listener: QueryListener): string; + startSubscription(options: SubscriptionOptions): number; stopQuery(queryId: string): void; getQueryWithPreviousResult(queryId: string, isOptimistic?: boolean): { previousResult: Object; @@ -451,7 +464,10 @@ export class QueryManager { queryFragments: FragmentDefinition[]; }; private collectResultBehaviorsFromUpdateQueries(updateQueries, mutationResult, isOptimistic?); - private fetchQueryOverInterface(queryId, options, network); + private transformQueryDocument(options); + private handleDiffQuery({queryDef, rootId, variables, fragmentMap, noFetch}); + private fetchRequest({requestId, queryId, query, querySS, options, fragmentMap, networkInterface}); + private fetchQueryOverInterface(queryId, options, networkInterface); private refetchQueryByName(queryName); private isDifferentResult(queryId, result); private broadcastQueries(); @@ -465,7 +481,7 @@ export * from '~apollo-client/QueryManager'; // Generated by typings // Source: node_modules/apollo-client/ObservableQuery.d.ts declare module '~apollo-client/ObservableQuery' { -import { WatchQueryOptions, FetchMoreQueryOptions } from '~apollo-client/watchQueryOptions'; +import { WatchQueryOptions, FetchMoreQueryOptions, GraphQLSubscriptionOptions } from '~apollo-client/watchQueryOptions'; import { Observable } from '~apollo-client/util/Observable'; import { QueryScheduler } from '~apollo-client/scheduler'; import { ApolloQueryResult } from '~apollo-client/index'; @@ -475,9 +491,14 @@ export interface FetchMoreOptions { queryVariables: Object; }) => Object; } +export interface UpdateQueryOptions { + queryVariables: Object; +} export class ObservableQuery extends Observable { refetch: (variables?: any) => Promise; fetchMore: (options: FetchMoreQueryOptions & FetchMoreOptions) => Promise; + startGraphQLSubscription: (options: GraphQLSubscriptionOptions) => number; + updateQuery: (mapFn: (previousQueryResult: any, options: UpdateQueryOptions) => any) => void; stopPolling: () => void; startPolling: (p: number) => void; options: WatchQueryOptions; @@ -517,6 +538,17 @@ export interface FetchMoreQueryOptions { [key: string]: any; }; } +export interface GraphQLSubscriptionOptions { + subscription: Document; + variables?: { + [key: string]: any; + }; + fragments?: FragmentDefinition[]; + updateQuery: (previousQueryResult: Object, options: { + subscriptionResult: Object; + queryVariables: Object; + }) => Object; +} } declare module 'apollo-client/watchQueryOptions' { export * from '~apollo-client/watchQueryOptions'; @@ -722,7 +754,7 @@ 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, SelectionSet } from 'graphql'; +import { Document, FragmentDefinition } from 'graphql'; import { print } from 'graphql-tag/printer'; import { createApolloStore, ApolloStore, createApolloReducer, ApolloReducerConfig } from '~apollo-client/store'; import { QueryManager } from '~apollo-client/QueryManager'; @@ -732,7 +764,7 @@ import { readQueryFromStore, readFragmentFromStore } from '~apollo-client/data/r import { writeQueryToStore, writeFragmentToStore } from '~apollo-client/data/writeToStore'; import { IdGetter } from '~apollo-client/data/extensions'; import { QueryTransformer, addTypenameToSelectionSet } from '~apollo-client/queries/queryTransform'; -import { MutationBehaviorReducerMap } from '~apollo-client/data/mutationResults'; +import { MutationBehavior, MutationBehaviorReducerMap, MutationQueryReducersMap } from '~apollo-client/data/mutationResults'; export { createNetworkInterface, addQueryMerging, createApolloStore, createApolloReducer, readQueryFromStore, readFragmentFromStore, addTypenameToSelectionSet as addTypename, writeQueryToStore, writeFragmentToStore, print as printAST }; export type ApolloQueryResult = { data: any; @@ -770,51 +802,21 @@ export default class ApolloClient { mutationBehaviorReducers?: MutationBehaviorReducerMap; batchInterval?: number; }); - watchQuery: (options: WatchQueryOptions) => ObservableQuery; - query: (options: WatchQueryOptions) => Promise<{ - data: any; - loading: boolean; - }>; - mutate: (options: { + watchQuery(options: WatchQueryOptions): ObservableQuery; + query(options: WatchQueryOptions): Promise; + mutate(options: { mutation: Document; variables?: Object; - resultBehaviors?: ({ - type: "ARRAY_INSERT"; - resultPath: (string | number)[]; - storePath: (string | number)[]; - where: "PREPEND" | "APPEND"; - } | { - type: "ARRAY_DELETE"; - storePath: (string | number)[]; - dataId: string; - } | { - type: "DELETE"; - dataId: string; - } | { - type: "QUERY_RESULT"; - queryVariables: any; - querySelectionSet: SelectionSet; - queryFragments: FragmentDefinition[]; - newResult: Object; - })[]; + resultBehaviors?: MutationBehavior[]; fragments?: FragmentDefinition[]; optimisticResponse?: Object; - updateQueries?: { - [queryName: string]: (previousResult: Object, options: { - mutationResult: Object; - queryName: Object; - queryVariables: Object; - }) => Object; - }; + updateQueries?: MutationQueryReducersMap; refetchQueries?: string[]; - }) => Promise<{ - data: any; - loading: boolean; - }>; + }): Promise; reducer(): Function; middleware: () => (store: ApolloStore) => (next: any) => (action: any) => any; initStore(): void; - private setStore; + private setStore(store); } } declare module 'apollo-client/index' {