From eef084e89f54113912b5278a0e1730f423a991fb Mon Sep 17 00:00:00 2001 From: Hugh Willson Date: Sun, 18 Nov 2018 08:53:37 -0500 Subject: [PATCH 01/11] Add client awareness name/version constructor options To assist with Engine's client awareness features, these changes allow `name` and `version` params to be passed into the `ApolloClient` constructor. These properties are then passed along via the defined `Link` chain, within the operation `context` made available to each `Link`. Any `Link` interested in using these values can pull them from the `context.clientAwareness` object. --- packages/apollo-client/src/ApolloClient.ts | 21 ++++++++++-- .../apollo-client/src/core/QueryManager.ts | 34 ++++++++++++------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/packages/apollo-client/src/ApolloClient.ts b/packages/apollo-client/src/ApolloClient.ts index 5f6d5cfb1ec..b002129eaec 100644 --- a/packages/apollo-client/src/ApolloClient.ts +++ b/packages/apollo-client/src/ApolloClient.ts @@ -49,6 +49,8 @@ export type ApolloClientOptions = { connectToDevTools?: boolean; queryDeduplication?: boolean; defaultOptions?: DefaultOptions; + name?: string; + version?: string; }; /** @@ -71,6 +73,7 @@ export default class ApolloClient implements DataProxy { private proxy: ApolloCache | undefined; private ssrMode: boolean; private resetStoreCallbacks: Array<() => Promise> = []; + private clientAwareness: { [key: string]: string } = {}; /** * Constructs an instance of {@link ApolloClient}. @@ -98,6 +101,8 @@ export default class ApolloClient implements DataProxy { connectToDevTools, queryDeduplication = true, defaultOptions, + name: clientAwarenessName, + version: clientAwarenessVersion, } = options; if (!link || !cache) { @@ -114,7 +119,7 @@ export default class ApolloClient implements DataProxy { const supportedDirectives = new ApolloLink( (operation: Operation, forward: NextLink) => { let result = supportedCache.get(operation.query); - if (! result) { + if (!result) { result = removeConnectionDirectiveFromDocument(operation.query); supportedCache.set(operation.query, result); supportedCache.set(result, result); @@ -191,7 +196,16 @@ export default class ApolloClient implements DataProxy { } } } + this.version = version; + + if (clientAwarenessName) { + this.clientAwareness.name = clientAwarenessName; + } + + if (clientAwarenessVersion) { + this.clientAwareness.version = clientAwarenessVersion; + } } /** * This watches the cache store of the query according to the options specified and @@ -402,6 +416,7 @@ export default class ApolloClient implements DataProxy { store: this.store, queryDeduplication: this.queryDeduplication, ssrMode: this.ssrMode, + clientAwareness: this.clientAwareness, onBroadcast: () => { if (this.devToolsHookCb) { this.devToolsHookCb({ @@ -460,8 +475,8 @@ export default class ApolloClient implements DataProxy { */ public clearStore(): Promise { const { queryManager } = this; - return Promise.resolve().then( - () => (queryManager ? queryManager.clearStore() : Promise.resolve(null)), + return Promise.resolve().then(() => + queryManager ? queryManager.clearStore() : Promise.resolve(null), ); } diff --git a/packages/apollo-client/src/core/QueryManager.ts b/packages/apollo-client/src/core/QueryManager.ts index 7c2583721db..0bf8d954b10 100644 --- a/packages/apollo-client/src/core/QueryManager.ts +++ b/packages/apollo-client/src/core/QueryManager.ts @@ -68,6 +68,7 @@ export class QueryManager { private deduplicator: ApolloLink; private queryDeduplication: boolean; + private clientAwareness: { [key: string]: string } = {}; private onBroadcast: () => void; @@ -94,19 +95,21 @@ export class QueryManager { store, onBroadcast = () => undefined, ssrMode = false, + clientAwareness = {}, }: { link: ApolloLink; queryDeduplication?: boolean; store: DataStore; onBroadcast?: () => void; ssrMode?: boolean; + clientAwareness?: { [key: string]: string }; }) { this.link = link; this.deduplicator = ApolloLink.from([new Deduplicator(), link]); this.queryDeduplication = queryDeduplication; this.dataStore = store; this.onBroadcast = onBroadcast; - + this.clientAwareness = clientAwareness; this.scheduler = new QueryScheduler({ queryManager: this, ssrMode }); } @@ -604,9 +607,11 @@ export class QueryManager { } if (observer.next) { - if (previouslyHadError || - !observableQuery || - observableQuery.isDifferentFromLastResult(resultFromStore)) { + if ( + previouslyHadError || + !observableQuery || + observableQuery.isDifferentFromLastResult(resultFromStore) + ) { try { observer.next(resultFromStore); } catch (e) { @@ -1212,15 +1217,17 @@ export class QueryManager { } private getQuery(queryId: string) { - return this.queries.get(queryId) || { - listeners: [], - invalidated: false, - document: null, - newData: null, - lastRequestId: null, - observableQuery: null, - subscriptions: [], - }; + return ( + this.queries.get(queryId) || { + listeners: [], + invalidated: false, + document: null, + newData: null, + lastRequestId: null, + observableQuery: null, + subscriptions: [], + } + ); } private setQuery(queryId: string, updater: (prev: QueryInfo) => any) { @@ -1268,6 +1275,7 @@ export class QueryManager { ); } }, + clientAwareness: this.clientAwareness, }, }; } From fdf86fda0ed868abd4750732cb80f433ee368a40 Mon Sep 17 00:00:00 2001 From: Hugh Willson Date: Sun, 18 Nov 2018 08:59:09 -0500 Subject: [PATCH 02/11] Add missing AC constructor `defaultOptions` docs --- packages/apollo-client/src/ApolloClient.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/apollo-client/src/ApolloClient.ts b/packages/apollo-client/src/ApolloClient.ts index b002129eaec..4a42002c6d0 100644 --- a/packages/apollo-client/src/ApolloClient.ts +++ b/packages/apollo-client/src/ApolloClient.ts @@ -90,6 +90,9 @@ export default class ApolloClient implements DataProxy { * @param queryDeduplication If set to false, a query will still be sent to the server even if a query * with identical parameters (query, variables, operationName) is already in flight. * + * @param defaultOptions Used to set application wide defaults for the + * options supplied to `watchQuery`, `query`, or + * `mutate`. */ constructor(options: ApolloClientOptions) { From 84c905ab64454e8b65b960577ec4cd890d0fa4e4 Mon Sep 17 00:00:00 2001 From: Hugh Willson Date: Sun, 18 Nov 2018 09:02:53 -0500 Subject: [PATCH 03/11] Docs for new contructor options --- packages/apollo-client/src/ApolloClient.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/apollo-client/src/ApolloClient.ts b/packages/apollo-client/src/ApolloClient.ts index 4a42002c6d0..eb17b6058f0 100644 --- a/packages/apollo-client/src/ApolloClient.ts +++ b/packages/apollo-client/src/ApolloClient.ts @@ -93,8 +93,13 @@ export default class ApolloClient implements DataProxy { * @param defaultOptions Used to set application wide defaults for the * options supplied to `watchQuery`, `query`, or * `mutate`. + * + * @param name A custom name that can be used to represent this client, when + * using Apollo Engine's client awareness features. + * + * @param version A custom version that can be used to represent this client, + * when using Apollo Engine's client awareness features. */ - constructor(options: ApolloClientOptions) { const { link, From ae5eda2e0debb1b6499d508eb60e09e0e65e2791 Mon Sep 17 00:00:00 2001 From: Hugh Willson Date: Mon, 19 Nov 2018 12:07:04 -0500 Subject: [PATCH 04/11] Add client awareness link chain test --- .../src/core/__tests__/QueryManager/index.ts | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/packages/apollo-client/src/core/__tests__/QueryManager/index.ts b/packages/apollo-client/src/core/__tests__/QueryManager/index.ts index 4fe0e3cef60..225af84aa41 100644 --- a/packages/apollo-client/src/core/__tests__/QueryManager/index.ts +++ b/packages/apollo-client/src/core/__tests__/QueryManager/index.ts @@ -52,15 +52,18 @@ describe('QueryManager', () => { const createQueryManager = ({ link, config = {}, + clientAwareness = {}, }: { link?: ApolloLink; config?: ApolloReducerConfig; + clientAwareness?: { [key: string]: string }; }) => { return new QueryManager({ link: link || mockSingleLink(), store: new DataStore( new InMemoryCache({ addTypename: false, ...config }), ), + clientAwareness, }); }; @@ -4771,4 +4774,51 @@ describe('QueryManager', () => { }, ); }); + + describe('client awareness', () => { + it('should pass client awareness settings into the link chain via context', done => { + const query = gql` + query { + author { + firstName + lastName + } + } + `; + + const data = { + author: { + firstName: 'John', + lastName: 'Smith', + }, + }; + + const link = mockSingleLink({ + request: { query }, + result: { data }, + }); + + const clientAwareness = { + name: 'Test', + version: '1.0.0', + }; + + const queryManager = createQueryManager({ + link, + clientAwareness, + }); + + const observable = queryManager.watchQuery({ + query, + fetchPolicy: 'no-cache', + }); + + observableToPromise({ observable }, result => { + const context = link.operation.getContext(); + expect(context.clientAwareness).toBeDefined(); + expect(context.clientAwareness).toEqual(clientAwareness); + done(); + }); + }); + }); }); From d6b9ccf89344ffec979b47ca65b53160fc3b47a3 Mon Sep 17 00:00:00 2001 From: Hugh Willson Date: Mon, 19 Nov 2018 12:47:28 -0500 Subject: [PATCH 05/11] Doc and changelog updates --- CHANGELOG.md | 9 +++++++++ docs/source/api/apollo-client.md | 2 ++ 2 files changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2233c272812..0546d582e7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ ## Apollo Client (vNext) +### Apollo Client (vNext) + +- The `ApolloClient` constructor has been updated to accept `name` and + `version` params, that can be used to support Apollo Server [Client Awareness](https://www.apollographql.com/docs/apollo-server/v2/features/metrics.html#Client-Awareness) + functionality. These client awareness properties are passed into the + defined Apollo Link chain, and are then ultimately sent out as custom + headers with outgoing requests.
+ [@hwillson](https://github.com/hwillson) in [#TODO]() + ## Apollo Client (2.4.6) ### Apollo Cache In-Memory (1.3.10) diff --git a/docs/source/api/apollo-client.md b/docs/source/api/apollo-client.md index ac93a678960..a214ffbc040 100644 --- a/docs/source/api/apollo-client.md +++ b/docs/source/api/apollo-client.md @@ -14,6 +14,8 @@ The Apollo Client constructor takes a small number of options, of which two are - `ssrForceFetchDelay`: determines the time interval before Apollo Client force fetchs queries after a server side render. - `connectToDevTools`: This argument allows the [Apollo Client Devtools](../features/developer-tooling.html) to connect to your application's Apollo Client. You can set this to be `true` to use the tools in production (they are on by default in dev mode). - `queryDeduplication`: If set to false, this argument will force a query to still be sent to the server even if a query with identical parameters (query, variables, operationName) is already in flight. +- `name`: A unique identifying client name that can be used to represent connections made using this instance of Apollo Client. Apollo Server leverages this property as part of its [Client Awareness](/docs/apollo-server/v2/features/metrics.html#Client-Awareness) functionality. +- `version`: A unique client version that can be used to represent connections made using this instance of Apollo Client. Apollo Server leverages this property as part of its [Client Awareness](/docs/apollo-server/v2/features/metrics.html#Client-Awareness) functionality. - `defaultOptions`: If you want to set application wide defaults for the options supplied to `watchQuery`, `query`, or `mutate`, you can pass them as a `defaultOptions` object. An example object looks like this: ```js From 678c2b9060f591b283249cd1afb685dc3c2ea809 Mon Sep 17 00:00:00 2001 From: Hugh Willson Date: Mon, 19 Nov 2018 12:57:04 -0500 Subject: [PATCH 06/11] Slight doc wording change --- packages/apollo-client/src/ApolloClient.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/apollo-client/src/ApolloClient.ts b/packages/apollo-client/src/ApolloClient.ts index eb17b6058f0..d10976cd0dd 100644 --- a/packages/apollo-client/src/ApolloClient.ts +++ b/packages/apollo-client/src/ApolloClient.ts @@ -95,10 +95,10 @@ export default class ApolloClient implements DataProxy { * `mutate`. * * @param name A custom name that can be used to represent this client, when - * using Apollo Engine's client awareness features. + * using Apollo client awareness features. * * @param version A custom version that can be used to represent this client, - * when using Apollo Engine's client awareness features. + * when using Apollo client awareness features. */ constructor(options: ApolloClientOptions) { const { From 9cb89420cad50b0f5c62f8fd2538ebccde31fb07 Mon Sep 17 00:00:00 2001 From: Evans Hauser Date: Tue, 20 Nov 2018 15:34:10 -0500 Subject: [PATCH 07/11] Update docs/source/api/apollo-client.md Co-Authored-By: hwillson --- docs/source/api/apollo-client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/api/apollo-client.md b/docs/source/api/apollo-client.md index a214ffbc040..df590b4d6ae 100644 --- a/docs/source/api/apollo-client.md +++ b/docs/source/api/apollo-client.md @@ -14,7 +14,7 @@ The Apollo Client constructor takes a small number of options, of which two are - `ssrForceFetchDelay`: determines the time interval before Apollo Client force fetchs queries after a server side render. - `connectToDevTools`: This argument allows the [Apollo Client Devtools](../features/developer-tooling.html) to connect to your application's Apollo Client. You can set this to be `true` to use the tools in production (they are on by default in dev mode). - `queryDeduplication`: If set to false, this argument will force a query to still be sent to the server even if a query with identical parameters (query, variables, operationName) is already in flight. -- `name`: A unique identifying client name that can be used to represent connections made using this instance of Apollo Client. Apollo Server leverages this property as part of its [Client Awareness](/docs/apollo-server/v2/features/metrics.html#Client-Awareness) functionality. +- `name`: A unique client name that can be used to identify connections made using this instance of Apollo Client. Apollo Server leverages this property as part of its [Client Awareness](/docs/apollo-server/v2/features/metrics.html#Client-Awareness) functionality. - `version`: A unique client version that can be used to represent connections made using this instance of Apollo Client. Apollo Server leverages this property as part of its [Client Awareness](/docs/apollo-server/v2/features/metrics.html#Client-Awareness) functionality. - `defaultOptions`: If you want to set application wide defaults for the options supplied to `watchQuery`, `query`, or `mutate`, you can pass them as a `defaultOptions` object. An example object looks like this: From 1a072d92efa44f1e5ce864eb85b40f26dc751095 Mon Sep 17 00:00:00 2001 From: Evans Hauser Date: Tue, 20 Nov 2018 15:34:46 -0500 Subject: [PATCH 08/11] Update docs/source/api/apollo-client.md Co-Authored-By: hwillson --- docs/source/api/apollo-client.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/source/api/apollo-client.md b/docs/source/api/apollo-client.md index df590b4d6ae..fc393aa3df8 100644 --- a/docs/source/api/apollo-client.md +++ b/docs/source/api/apollo-client.md @@ -15,7 +15,7 @@ The Apollo Client constructor takes a small number of options, of which two are - `connectToDevTools`: This argument allows the [Apollo Client Devtools](../features/developer-tooling.html) to connect to your application's Apollo Client. You can set this to be `true` to use the tools in production (they are on by default in dev mode). - `queryDeduplication`: If set to false, this argument will force a query to still be sent to the server even if a query with identical parameters (query, variables, operationName) is already in flight. - `name`: A unique client name that can be used to identify connections made using this instance of Apollo Client. Apollo Server leverages this property as part of its [Client Awareness](/docs/apollo-server/v2/features/metrics.html#Client-Awareness) functionality. -- `version`: A unique client version that can be used to represent connections made using this instance of Apollo Client. Apollo Server leverages this property as part of its [Client Awareness](/docs/apollo-server/v2/features/metrics.html#Client-Awareness) functionality. +- `version`: A unique client version that can be used to differentiate connections made using this instance of Apollo Client from others with the same `name`. Apollo Server leverages this property as part of its [Client Awareness](/docs/apollo-server/v2/features/metrics.html#Client-Awareness) functionality. - `defaultOptions`: If you want to set application wide defaults for the options supplied to `watchQuery`, `query`, or `mutate`, you can pass them as a `defaultOptions` object. An example object looks like this: ```js From 0a16f1782771fecdf494ab3dd7058eb6b028f6da Mon Sep 17 00:00:00 2001 From: Evans Hauser Date: Tue, 20 Nov 2018 15:35:06 -0500 Subject: [PATCH 09/11] Update packages/apollo-client/src/ApolloClient.ts Co-Authored-By: hwillson --- packages/apollo-client/src/ApolloClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/apollo-client/src/ApolloClient.ts b/packages/apollo-client/src/ApolloClient.ts index d10976cd0dd..9e5d45e97ef 100644 --- a/packages/apollo-client/src/ApolloClient.ts +++ b/packages/apollo-client/src/ApolloClient.ts @@ -94,7 +94,7 @@ export default class ApolloClient implements DataProxy { * options supplied to `watchQuery`, `query`, or * `mutate`. * - * @param name A custom name that can be used to represent this client, when + * @param name A custom name that can be used to identify this client, when * using Apollo client awareness features. * * @param version A custom version that can be used to represent this client, From 45c7dee58c44f3c5247b65b5e784c9e67fc9508e Mon Sep 17 00:00:00 2001 From: Evans Hauser Date: Tue, 20 Nov 2018 15:35:16 -0500 Subject: [PATCH 10/11] Update packages/apollo-client/src/ApolloClient.ts Co-Authored-By: hwillson --- packages/apollo-client/src/ApolloClient.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/apollo-client/src/ApolloClient.ts b/packages/apollo-client/src/ApolloClient.ts index 9e5d45e97ef..96013bd6298 100644 --- a/packages/apollo-client/src/ApolloClient.ts +++ b/packages/apollo-client/src/ApolloClient.ts @@ -97,7 +97,7 @@ export default class ApolloClient implements DataProxy { * @param name A custom name that can be used to identify this client, when * using Apollo client awareness features. * - * @param version A custom version that can be used to represent this client, + * @param version A custom version that can be used to identify this client, * when using Apollo client awareness features. */ constructor(options: ApolloClientOptions) { From 59734395bfd838930d913d3d2d69980de02a4fb3 Mon Sep 17 00:00:00 2001 From: Hugh Willson Date: Tue, 20 Nov 2018 20:45:07 -0500 Subject: [PATCH 11/11] Doc, code and changelog updates based on code review --- CHANGELOG.md | 2 +- docs/source/api/apollo-client.md | 4 ++-- packages/apollo-client/src/ApolloClient.ts | 9 ++++++--- packages/apollo-client/src/core/QueryManager.ts | 4 ++-- 4 files changed, 11 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0546d582e7c..63a487f6be2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ functionality. These client awareness properties are passed into the defined Apollo Link chain, and are then ultimately sent out as custom headers with outgoing requests.
- [@hwillson](https://github.com/hwillson) in [#TODO]() + [@hwillson](https://github.com/hwillson) in [#4154](https://github.com/apollographql/apollo-client/pull/4154) ## Apollo Client (2.4.6) diff --git a/docs/source/api/apollo-client.md b/docs/source/api/apollo-client.md index fc393aa3df8..0d5db73e342 100644 --- a/docs/source/api/apollo-client.md +++ b/docs/source/api/apollo-client.md @@ -14,8 +14,8 @@ The Apollo Client constructor takes a small number of options, of which two are - `ssrForceFetchDelay`: determines the time interval before Apollo Client force fetchs queries after a server side render. - `connectToDevTools`: This argument allows the [Apollo Client Devtools](../features/developer-tooling.html) to connect to your application's Apollo Client. You can set this to be `true` to use the tools in production (they are on by default in dev mode). - `queryDeduplication`: If set to false, this argument will force a query to still be sent to the server even if a query with identical parameters (query, variables, operationName) is already in flight. -- `name`: A unique client name that can be used to identify connections made using this instance of Apollo Client. Apollo Server leverages this property as part of its [Client Awareness](/docs/apollo-server/v2/features/metrics.html#Client-Awareness) functionality. -- `version`: A unique client version that can be used to differentiate connections made using this instance of Apollo Client from others with the same `name`. Apollo Server leverages this property as part of its [Client Awareness](/docs/apollo-server/v2/features/metrics.html#Client-Awareness) functionality. +- `name`: A custom name that can be used to identify this client, e.g. "iOS". Apollo Server leverages this property as part of its [Client Awareness](/docs/apollo-server/v2/features/metrics.html#Client-Awareness) functionality. +- `version`: A custom version that can be used to identify this client, when using Apollo client awareness features. This is the version of your client, which you may want to increment on new builds. This is NOT the version of Apollo Client that you are using. Apollo Server leverages this property as part of its [Client Awareness](/docs/apollo-server/v2/features/metrics.html#Client-Awareness) functionality. - `defaultOptions`: If you want to set application wide defaults for the options supplied to `watchQuery`, `query`, or `mutate`, you can pass them as a `defaultOptions` object. An example object looks like this: ```js diff --git a/packages/apollo-client/src/ApolloClient.ts b/packages/apollo-client/src/ApolloClient.ts index 96013bd6298..6f9863b226b 100644 --- a/packages/apollo-client/src/ApolloClient.ts +++ b/packages/apollo-client/src/ApolloClient.ts @@ -73,7 +73,7 @@ export default class ApolloClient implements DataProxy { private proxy: ApolloCache | undefined; private ssrMode: boolean; private resetStoreCallbacks: Array<() => Promise> = []; - private clientAwareness: { [key: string]: string } = {}; + private clientAwareness: Record = {}; /** * Constructs an instance of {@link ApolloClient}. @@ -95,10 +95,13 @@ export default class ApolloClient implements DataProxy { * `mutate`. * * @param name A custom name that can be used to identify this client, when - * using Apollo client awareness features. + * using Apollo client awareness features. E.g. "iOS". * * @param version A custom version that can be used to identify this client, - * when using Apollo client awareness features. + * when using Apollo client awareness features. This is the + * version of your client, which you may want to increment on + * new builds. This is NOT the version of Apollo Client that + * you are using. */ constructor(options: ApolloClientOptions) { const { diff --git a/packages/apollo-client/src/core/QueryManager.ts b/packages/apollo-client/src/core/QueryManager.ts index 0bf8d954b10..0f2239a256d 100644 --- a/packages/apollo-client/src/core/QueryManager.ts +++ b/packages/apollo-client/src/core/QueryManager.ts @@ -68,7 +68,7 @@ export class QueryManager { private deduplicator: ApolloLink; private queryDeduplication: boolean; - private clientAwareness: { [key: string]: string } = {}; + private clientAwareness: Record = {}; private onBroadcast: () => void; @@ -102,7 +102,7 @@ export class QueryManager { store: DataStore; onBroadcast?: () => void; ssrMode?: boolean; - clientAwareness?: { [key: string]: string }; + clientAwareness?: Record; }) { this.link = link; this.deduplicator = ApolloLink.from([new Deduplicator(), link]);