Skip to content

Commit 453e948

Browse files
authored
Add client awareness support (#4154)
* 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. * Add missing AC constructor `defaultOptions` docs * Docs for new contructor options * Add client awareness link chain test * Doc and changelog updates * Slight doc wording change * Update docs/source/api/apollo-client.md Co-Authored-By: hwillson <[email protected]> * Update docs/source/api/apollo-client.md Co-Authored-By: hwillson <[email protected]> * Update packages/apollo-client/src/ApolloClient.ts Co-Authored-By: hwillson <[email protected]> * Update packages/apollo-client/src/ApolloClient.ts Co-Authored-By: hwillson <[email protected]> * Doc, code and changelog updates based on code review
1 parent 11aad81 commit 453e948

File tree

5 files changed

+112
-17
lines changed

5 files changed

+112
-17
lines changed

Diff for: CHANGELOG.md

+9
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,15 @@
22

33
## Apollo Client (vNext)
44

5+
### Apollo Client (vNext)
6+
7+
- The `ApolloClient` constructor has been updated to accept `name` and
8+
`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)
9+
functionality. These client awareness properties are passed into the
10+
defined Apollo Link chain, and are then ultimately sent out as custom
11+
headers with outgoing requests. <br/>
12+
[@hwillson](https://github.com/hwillson) in [#4154](https://github.com/apollographql/apollo-client/pull/4154)
13+
514
## Apollo Client (2.4.6)
615

716
### Apollo Cache In-Memory (1.3.10)

Diff for: docs/source/api/apollo-client.md

+2
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ The Apollo Client constructor takes a small number of options, of which two are
1414
- `ssrForceFetchDelay`: determines the time interval before Apollo Client force fetchs queries after a server side render.
1515
- `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).
1616
- `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.
17+
- `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.
18+
- `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.
1719
- `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:
1820

1921
```js

Diff for: packages/apollo-client/src/ApolloClient.ts

+30-4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ export type ApolloClientOptions<TCacheShape> = {
4949
connectToDevTools?: boolean;
5050
queryDeduplication?: boolean;
5151
defaultOptions?: DefaultOptions;
52+
name?: string;
53+
version?: string;
5254
};
5355

5456
/**
@@ -71,6 +73,7 @@ export default class ApolloClient<TCacheShape> implements DataProxy {
7173
private proxy: ApolloCache<TCacheShape> | undefined;
7274
private ssrMode: boolean;
7375
private resetStoreCallbacks: Array<() => Promise<any>> = [];
76+
private clientAwareness: Record<string, string> = {};
7477

7578
/**
7679
* Constructs an instance of {@link ApolloClient}.
@@ -87,8 +90,19 @@ export default class ApolloClient<TCacheShape> implements DataProxy {
8790
* @param queryDeduplication If set to false, a query will still be sent to the server even if a query
8891
* with identical parameters (query, variables, operationName) is already in flight.
8992
*
93+
* @param defaultOptions Used to set application wide defaults for the
94+
* options supplied to `watchQuery`, `query`, or
95+
* `mutate`.
96+
*
97+
* @param name A custom name that can be used to identify this client, when
98+
* using Apollo client awareness features. E.g. "iOS".
99+
*
100+
* @param version A custom version that can be used to identify this client,
101+
* when using Apollo client awareness features. This is the
102+
* version of your client, which you may want to increment on
103+
* new builds. This is NOT the version of Apollo Client that
104+
* you are using.
90105
*/
91-
92106
constructor(options: ApolloClientOptions<TCacheShape>) {
93107
const {
94108
link,
@@ -98,6 +112,8 @@ export default class ApolloClient<TCacheShape> implements DataProxy {
98112
connectToDevTools,
99113
queryDeduplication = true,
100114
defaultOptions,
115+
name: clientAwarenessName,
116+
version: clientAwarenessVersion,
101117
} = options;
102118

103119
if (!link || !cache) {
@@ -114,7 +130,7 @@ export default class ApolloClient<TCacheShape> implements DataProxy {
114130
const supportedDirectives = new ApolloLink(
115131
(operation: Operation, forward: NextLink) => {
116132
let result = supportedCache.get(operation.query);
117-
if (! result) {
133+
if (!result) {
118134
result = removeConnectionDirectiveFromDocument(operation.query);
119135
supportedCache.set(operation.query, result);
120136
supportedCache.set(result, result);
@@ -191,7 +207,16 @@ export default class ApolloClient<TCacheShape> implements DataProxy {
191207
}
192208
}
193209
}
210+
194211
this.version = version;
212+
213+
if (clientAwarenessName) {
214+
this.clientAwareness.name = clientAwarenessName;
215+
}
216+
217+
if (clientAwarenessVersion) {
218+
this.clientAwareness.version = clientAwarenessVersion;
219+
}
195220
}
196221
/**
197222
* This watches the cache store of the query according to the options specified and
@@ -402,6 +427,7 @@ export default class ApolloClient<TCacheShape> implements DataProxy {
402427
store: this.store,
403428
queryDeduplication: this.queryDeduplication,
404429
ssrMode: this.ssrMode,
430+
clientAwareness: this.clientAwareness,
405431
onBroadcast: () => {
406432
if (this.devToolsHookCb) {
407433
this.devToolsHookCb({
@@ -460,8 +486,8 @@ export default class ApolloClient<TCacheShape> implements DataProxy {
460486
*/
461487
public clearStore(): Promise<void | null> {
462488
const { queryManager } = this;
463-
return Promise.resolve().then(
464-
() => (queryManager ? queryManager.clearStore() : Promise.resolve(null)),
489+
return Promise.resolve().then(() =>
490+
queryManager ? queryManager.clearStore() : Promise.resolve(null),
465491
);
466492
}
467493

Diff for: packages/apollo-client/src/core/QueryManager.ts

+21-13
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ export class QueryManager<TStore> {
6868

6969
private deduplicator: ApolloLink;
7070
private queryDeduplication: boolean;
71+
private clientAwareness: Record<string, string> = {};
7172

7273
private onBroadcast: () => void;
7374

@@ -94,19 +95,21 @@ export class QueryManager<TStore> {
9495
store,
9596
onBroadcast = () => undefined,
9697
ssrMode = false,
98+
clientAwareness = {},
9799
}: {
98100
link: ApolloLink;
99101
queryDeduplication?: boolean;
100102
store: DataStore<TStore>;
101103
onBroadcast?: () => void;
102104
ssrMode?: boolean;
105+
clientAwareness?: Record<string, string>;
103106
}) {
104107
this.link = link;
105108
this.deduplicator = ApolloLink.from([new Deduplicator(), link]);
106109
this.queryDeduplication = queryDeduplication;
107110
this.dataStore = store;
108111
this.onBroadcast = onBroadcast;
109-
112+
this.clientAwareness = clientAwareness;
110113
this.scheduler = new QueryScheduler({ queryManager: this, ssrMode });
111114
}
112115

@@ -604,9 +607,11 @@ export class QueryManager<TStore> {
604607
}
605608

606609
if (observer.next) {
607-
if (previouslyHadError ||
608-
!observableQuery ||
609-
observableQuery.isDifferentFromLastResult(resultFromStore)) {
610+
if (
611+
previouslyHadError ||
612+
!observableQuery ||
613+
observableQuery.isDifferentFromLastResult(resultFromStore)
614+
) {
610615
try {
611616
observer.next(resultFromStore);
612617
} catch (e) {
@@ -1212,15 +1217,17 @@ export class QueryManager<TStore> {
12121217
}
12131218

12141219
private getQuery(queryId: string) {
1215-
return this.queries.get(queryId) || {
1216-
listeners: [],
1217-
invalidated: false,
1218-
document: null,
1219-
newData: null,
1220-
lastRequestId: null,
1221-
observableQuery: null,
1222-
subscriptions: [],
1223-
};
1220+
return (
1221+
this.queries.get(queryId) || {
1222+
listeners: [],
1223+
invalidated: false,
1224+
document: null,
1225+
newData: null,
1226+
lastRequestId: null,
1227+
observableQuery: null,
1228+
subscriptions: [],
1229+
}
1230+
);
12241231
}
12251232

12261233
private setQuery(queryId: string, updater: (prev: QueryInfo) => any) {
@@ -1268,6 +1275,7 @@ export class QueryManager<TStore> {
12681275
);
12691276
}
12701277
},
1278+
clientAwareness: this.clientAwareness,
12711279
},
12721280
};
12731281
}

Diff for: packages/apollo-client/src/core/__tests__/QueryManager/index.ts

+50
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,18 @@ describe('QueryManager', () => {
5252
const createQueryManager = ({
5353
link,
5454
config = {},
55+
clientAwareness = {},
5556
}: {
5657
link?: ApolloLink;
5758
config?: ApolloReducerConfig;
59+
clientAwareness?: { [key: string]: string };
5860
}) => {
5961
return new QueryManager({
6062
link: link || mockSingleLink(),
6163
store: new DataStore(
6264
new InMemoryCache({ addTypename: false, ...config }),
6365
),
66+
clientAwareness,
6467
});
6568
};
6669

@@ -4771,4 +4774,51 @@ describe('QueryManager', () => {
47714774
},
47724775
);
47734776
});
4777+
4778+
describe('client awareness', () => {
4779+
it('should pass client awareness settings into the link chain via context', done => {
4780+
const query = gql`
4781+
query {
4782+
author {
4783+
firstName
4784+
lastName
4785+
}
4786+
}
4787+
`;
4788+
4789+
const data = {
4790+
author: {
4791+
firstName: 'John',
4792+
lastName: 'Smith',
4793+
},
4794+
};
4795+
4796+
const link = mockSingleLink({
4797+
request: { query },
4798+
result: { data },
4799+
});
4800+
4801+
const clientAwareness = {
4802+
name: 'Test',
4803+
version: '1.0.0',
4804+
};
4805+
4806+
const queryManager = createQueryManager({
4807+
link,
4808+
clientAwareness,
4809+
});
4810+
4811+
const observable = queryManager.watchQuery<any>({
4812+
query,
4813+
fetchPolicy: 'no-cache',
4814+
});
4815+
4816+
observableToPromise({ observable }, result => {
4817+
const context = link.operation.getContext();
4818+
expect(context.clientAwareness).toBeDefined();
4819+
expect(context.clientAwareness).toEqual(clientAwareness);
4820+
done();
4821+
});
4822+
});
4823+
});
47744824
});

0 commit comments

Comments
 (0)