Skip to content
This repository was archived by the owner on Sep 2, 2022. It is now read-only.

Commit fae5ed0

Browse files
authored
Merge pull request #23 from apilytics/send-geo-ip
Send user's IP address with metrics
2 parents 8ecfc00 + 8f67e94 commit fae5ed0

7 files changed

Lines changed: 72 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Send user's IP address with metrics. Used for visualization aggregate geolocation data.
13+
The IP is never stored, and it is never sent to 3rd parties.
14+
1015
## [1.4.1] - 2022-02-20
1116

1217
### Added

packages/core/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ const myApilyticsMiddleware = async (req, handler) => {
4444

4545
const timer = milliSecondTimer();
4646
const res = await handler(req);
47+
4748
sendApilyticsMetrics({
4849
apiKey,
4950
path: req.path,
@@ -53,6 +54,7 @@ const myApilyticsMiddleware = async (req, handler) => {
5354
requestSize: req.bodyBytes.length,
5455
responseSize: res.bodyBytes.length,
5556
userAgent: req.headers['user-agent'],
57+
ip: req.headers['x-forwarded-for']?.split(',')[0].trim(),
5658
timeMillis: timer(),
5759
});
5860
return res;

packages/core/src/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ interface Params {
1414
requestSize?: number;
1515
responseSize?: number;
1616
userAgent?: string;
17+
ip?: string;
1718
apilyticsIntegration?: string;
1819
integratedLibrary?: string;
1920
}
@@ -38,6 +39,8 @@ interface Params {
3839
* @param params.responseSize - Size of the sent HTTP response's body in bytes.
3940
* @param params.userAgent - Value of the `User-Agent` header from the user's
4041
* HTTP request.
42+
* @param params.ip - User's IP address (used for geolocation,
43+
* never stored nor sent to 3rd parties).
4144
* @param params.apilyticsIntegration - Name of the Apilytics integration that's
4245
* calling this, e.g. 'apilytics-node-express'.
4346
* No need to pass this when calling from user code.
@@ -49,6 +52,7 @@ interface Params {
4952
*
5053
* const timer = milliSecondTimer();
5154
* const res = await handler(req);
55+
*
5256
* sendApilyticsMetrics({
5357
* apikey: "<your-api-key>",
5458
* path: req.path,
@@ -58,6 +62,7 @@ interface Params {
5862
* requestSize: req.bodyBytes.length,
5963
* responseSize: res.bodyBytes.length,
6064
* userAgent: req.headers['user-agent'],
65+
* ip: req.headers['x-forwarded-for']?.split(',')[0].trim(),
6166
* timeMillis: timer(),
6267
* });
6368
*/
@@ -71,6 +76,7 @@ export const sendApilyticsMetrics = ({
7176
requestSize,
7277
responseSize,
7378
userAgent,
79+
ip,
7480
apilyticsIntegration,
7581
integratedLibrary,
7682
}: Params): void => {
@@ -86,6 +92,7 @@ export const sendApilyticsMetrics = ({
8692
requestSize,
8793
responseSize,
8894
userAgent: userAgent || undefined,
95+
ip: ip || undefined,
8996
cpuUsage,
9097
memoryUsage,
9198
memoryTotal,

packages/express/__tests__/index.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,31 @@ describe('apilyticsMiddleware()', () => {
178178
expect(data.userAgent).toEqual('some agent');
179179
});
180180

181+
it('should send IP', async () => {
182+
const agent = createAgent({ apiKey });
183+
let response = await agent
184+
.get('/dummy')
185+
.set('X-Forwarded-For', '127.0.0.1');
186+
expect(response.status).toEqual(200);
187+
188+
await flushTimers();
189+
190+
expect(requestSpy).toHaveBeenCalledTimes(1);
191+
let data = JSON.parse(clientRequestMock.write.mock.calls[0]);
192+
expect(data.ip).toEqual('127.0.0.1');
193+
194+
response = await agent
195+
.get('/dummy')
196+
.set('X-Forwarded-For', '127.0.0.2,127.0.0.3');
197+
expect(response.status).toEqual(200);
198+
199+
await flushTimers();
200+
201+
expect(requestSpy).toHaveBeenCalledTimes(2);
202+
data = JSON.parse(clientRequestMock.write.mock.calls[1]);
203+
expect(data.ip).toEqual('127.0.0.2');
204+
});
205+
181206
it('should handle zero request and response sizes', async () => {
182207
const agent = createAgent({ apiKey });
183208
const response = await agent.post('/empty');

packages/express/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,10 @@ export const apilyticsMiddleware = (
5959
requestSize,
6060
responseSize,
6161
userAgent: req.headers['user-agent'],
62+
// Type assertion since the value cannot be an array.
63+
ip: (req.headers['x-forwarded-for'] as string | undefined)
64+
?.split(',')[0]
65+
.trim(),
6266
timeMillis: timer(),
6367
apilyticsIntegration: 'apilytics-node-express',
6468
integratedLibrary: EXPRESS_VERSION

packages/next/__tests__/index.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,31 @@ describe('withApilytics()', () => {
198198
expect(data.userAgent).toEqual('some agent');
199199
});
200200

201+
it('should send IP', async () => {
202+
const agent = createAgent({ apiKey });
203+
let response = await agent
204+
.get('/dummy')
205+
.set('X-Forwarded-For', '127.0.0.1');
206+
expect(response.status).toEqual(200);
207+
208+
await flushTimers();
209+
210+
expect(requestSpy).toHaveBeenCalledTimes(1);
211+
let data = JSON.parse(clientRequestMock.write.mock.calls[0]);
212+
expect(data.ip).toEqual('127.0.0.1');
213+
214+
response = await agent
215+
.get('/dummy')
216+
.set('X-Forwarded-For', '127.0.0.2,127.0.0.3');
217+
expect(response.status).toEqual(200);
218+
219+
await flushTimers();
220+
221+
expect(requestSpy).toHaveBeenCalledTimes(2);
222+
data = JSON.parse(clientRequestMock.write.mock.calls[1]);
223+
expect(data.ip).toEqual('127.0.0.2');
224+
});
225+
201226
it('should handle zero request and response sizes', async () => {
202227
const agent = createAgent({ apiKey });
203228
const response = await agent.post('/empty');

packages/next/src/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ export const withApilytics = <T>(
7474
requestSize,
7575
responseSize,
7676
userAgent: req.headers['user-agent'],
77+
// Type assertion since the value cannot be an array.
78+
ip: (req.headers['x-forwarded-for'] as string | undefined)
79+
?.split(',')[0]
80+
.trim(),
7781
timeMillis: timer(),
7882
apilyticsIntegration: 'apilytics-node-next',
7983
integratedLibrary: NEXT_VERSION ? `next/${NEXT_VERSION}` : undefined,

0 commit comments

Comments
 (0)