Skip to content

Commit 9b3840a

Browse files
committed
Merge branch 'main' of github.com:Flagsmith/flagsmith-nodejs-client into fix/handle-environment-documentation-pagination
2 parents acb761f + ef2b97a commit 9b3840a

File tree

8 files changed

+73
-7
lines changed

8 files changed

+73
-7
lines changed

sdk/analytics.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { pino, Logger } from 'pino';
22
import { Fetch } from './types.js';
33
import { FlagsmithConfig } from './types.js';
4+
import { getUserAgent } from './utils.js';
45

56
export const ANALYTICS_ENDPOINT = './analytics/flags/';
67

@@ -69,7 +70,8 @@ export class AnalyticsProcessor {
6970
signal: AbortSignal.timeout(this.requestTimeoutMs),
7071
headers: {
7172
'Content-Type': 'application/json',
72-
'X-Environment-Key': this.environmentKey
73+
'X-Environment-Key': this.environmentKey,
74+
'User-Agent': getUserAgent()
7375
}
7476
});
7577
await this.currentFlush;

sdk/index.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { FlagsmithAPIError } from './errors.js';
1414

1515
import { DefaultFlag, Flags } from './models.js';
1616
import { EnvironmentDataPollingManager } from './polling_manager.js';
17-
import { Deferred, generateIdentitiesData, retryFetch } from './utils.js';
17+
import { Deferred, generateIdentitiesData, getUserAgent, retryFetch } from './utils.js';
1818
import { SegmentModel } from '../flagsmith-engine/index.js';
1919
import { getIdentitySegments } from '../flagsmith-engine/segments/evaluators.js';
2020
import {
@@ -335,6 +335,8 @@ export class Flagsmith {
335335
headers['X-Environment-Key'] = this.environmentKey as string;
336336
}
337337

338+
headers['User-Agent'] = getUserAgent();
339+
338340
if (this.customHeaders) {
339341
for (const [k, v] of Object.entries(this.customHeaders)) {
340342
headers[k] = v;

sdk/utils.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ import { Dispatcher } from 'undici-types';
33

44
type Traits = { [key: string]: TraitConfig | FlagsmithTraitValue };
55

6+
const FLAGSMITH_USER_AGENT = 'flagsmith-nodejs-sdk';
7+
const FLAGSMITH_UNKNOWN_VERSION = 'unknown';
8+
69
export function isTraitConfig(
710
traitValue: TraitConfig | FlagsmithTraitValue
811
): traitValue is TraitConfig {
@@ -102,3 +105,13 @@ export class Deferred<T> {
102105
this.rejectPromise(reason);
103106
}
104107
}
108+
109+
export function getUserAgent(): string {
110+
try {
111+
const packageJson = require('../package.json');
112+
const version = packageJson?.version;
113+
return version ? `${FLAGSMITH_USER_AGENT}/${version}` : FLAGSMITH_UNKNOWN_VERSION;
114+
} catch {
115+
return FLAGSMITH_UNKNOWN_VERSION;
116+
}
117+
}

tests/sdk/analytics.test.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { getUserAgent } from '../../sdk/utils.js';
12
import { analyticsProcessor, fetch } from './utils.js';
23

34
test('test_analytics_processor_track_feature_updates_analytics_data', () => {
@@ -26,7 +27,11 @@ test('test_analytics_processor_flush_post_request_data_match_ananlytics_data', a
2627
'http://testUrl/analytics/flags/',
2728
expect.objectContaining({
2829
body: '{"myFeature1":1,"myFeature2":1}',
29-
headers: { 'Content-Type': 'application/json', 'X-Environment-Key': 'test-key' },
30+
headers: {
31+
'Content-Type': 'application/json',
32+
'X-Environment-Key': 'test-key',
33+
'User-Agent': getUserAgent()
34+
},
3035
method: 'POST'
3136
})
3237
);

tests/sdk/flagsmith-environment-flags.test.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import Flagsmith from '../../sdk/index.js';
22
import { environmentJSON, environmentModel, flagsJSON, flagsmith, fetch } from './utils.js';
33
import { DefaultFlag } from '../../sdk/models.js';
4+
import { getUserAgent } from '../../sdk/utils.js';
45

56
vi.mock('../../sdk/polling_manager');
67

@@ -72,6 +73,33 @@ test('test_getFeatureValue', async () => {
7273
expect(featureValue).toBe('some-value');
7374
});
7475

76+
test('test_user_agent_is_set_when_fetching_environment_flags', async () => {
77+
const defaultFlag = new DefaultFlag('some-default-value', true);
78+
79+
const defaultFlagHandler = (featureName: string) => defaultFlag;
80+
81+
const flg = flagsmith({
82+
environmentKey: 'key',
83+
defaultFlagHandler: defaultFlagHandler,
84+
enableAnalytics: true
85+
});
86+
const flags = await flg.getEnvironmentFlags();
87+
const featureValue = flags.getFeatureValue('some_feature');
88+
89+
expect(featureValue).toBe('some-value');
90+
expect(fetch).toHaveBeenCalledWith(
91+
`https://edge.api.flagsmith.com/api/v1/flags/`,
92+
expect.objectContaining({
93+
method: 'GET',
94+
headers: {
95+
'Content-Type': 'application/json',
96+
'X-Environment-Key': 'key',
97+
'User-Agent': getUserAgent()
98+
}
99+
})
100+
);
101+
});
102+
75103
test('test_throws_when_no_default_flag_handler_after_multiple_API_errors', async () => {
76104
fetch.mockRejectedValue('Error during fetching the API response');
77105

tests/sdk/flagsmith-identity-flags.test.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
badFetch
1010
} from './utils.js';
1111
import { DefaultFlag } from '../../sdk/models.js';
12+
import { getUserAgent } from '../../sdk/utils.js';
1213

1314
vi.mock('../../sdk/polling_manager');
1415

@@ -150,7 +151,11 @@ test('test_transient_identity', async () => {
150151
`https://edge.api.flagsmith.com/api/v1/identities/`,
151152
expect.objectContaining({
152153
method: 'POST',
153-
headers: { 'Content-Type': 'application/json', 'X-Environment-Key': 'sometestfakekey' },
154+
headers: {
155+
'Content-Type': 'application/json',
156+
'X-Environment-Key': 'sometestfakekey',
157+
'User-Agent': getUserAgent()
158+
},
154159
body: JSON.stringify({ identifier, traits: traitsInRequest, transient })
155160
})
156161
);
@@ -191,7 +196,11 @@ test('test_identity_with_transient_traits', async () => {
191196
`https://edge.api.flagsmith.com/api/v1/identities/`,
192197
expect.objectContaining({
193198
method: 'POST',
194-
headers: { 'Content-Type': 'application/json', 'X-Environment-Key': 'sometestfakekey' },
199+
headers: {
200+
'Content-Type': 'application/json',
201+
'X-Environment-Key': 'sometestfakekey',
202+
'User-Agent': getUserAgent()
203+
},
195204
body: JSON.stringify({ identifier, traits: traitsInRequest })
196205
})
197206
);

tests/sdk/flagsmith.test.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import {
99
badFetch
1010
} from './utils.js';
1111
import { DefaultFlag, Flags } from '../../sdk/models.js';
12-
import { delay } from '../../sdk/utils.js';
12+
import { delay, getUserAgent } from '../../sdk/utils.js';
1313
import { EnvironmentModel } from '../../flagsmith-engine/environments/models.js';
1414
import { BaseOfflineHandler } from '../../sdk/offline_handlers.js';
1515
import { Agent } from 'undici';
@@ -512,3 +512,10 @@ test('getIdentityFlags succeeds if initial fetch failed then succeeded', async (
512512
const flags2 = await flg.getIdentityFlags('test-user');
513513
expect(flags2.isFeatureEnabled('some_feature')).toBe(true);
514514
});
515+
516+
test('get_user_agent_extracts_version_from_package_json', async () => {
517+
const userAgent = getUserAgent();
518+
const packageJson = require('../../package.json');
519+
520+
expect(userAgent).toBe(`flagsmith-nodejs-sdk/${packageJson.version}`);
521+
});

0 commit comments

Comments
 (0)