Skip to content

Commit df42f0c

Browse files
Merge branch 'split_changes_version' into SDKS_8273_reuse_matchers_debug_logs
2 parents 0a5008e + 9f9561e commit df42f0c

File tree

9 files changed

+30
-22
lines changed

9 files changed

+30
-22
lines changed

CHANGES.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
1.14.0 (April XX, 2024)
2+
- Added support for Semver matchers.
23
- Updated impression label to 'unsupported matcher type' when the matcher type is not supported by the SDK.
4+
- Updated Split API client to include the flags spec version query parameter for the `splitChanges` and `auth` endpoints.
35

46
1.13.1 (January 10, 2024)
57
- Updated client `destroy` method to release SDK key immediately and avoid unexpected warning logs when a factory is created with the same SDK key after the previous one was destroyed.

src/evaluator/matchers/__tests__/mocks/equal-to-semver.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,5 @@
44
88.88.88,88.88.88,true
55
1.2.3----RC-SNAPSHOT.12.9.1--.12,1.2.3----RC-SNAPSHOT.12.9.1--.12,true
66
10.2.3-DEV-SNAPSHOT,10.2.3-SNAPSHOT-123,false
7+
00.01.002-0003.00004,0.1.2-3.4,true
8+
00.01.002-0003.0000z,0.1.2-3.z,false

src/evaluator/matchers/__tests__/mocks/invalid-semantic-versions.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,5 @@ beta
2424
-1.0.3-gamma+b7718
2525
+justmeta
2626
99999999999999999999999.999999999999999999.99999999999999999----RC-SNAPSHOT.12.09.1--------------------------------..12
27+
1.1.1+
28+
1.1.1-

src/services/__tests__/splitApi.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ describe('splitApi', () => {
2727
splitApi.fetchAuth(['key1', 'key2']);
2828
let [url, { headers }] = fetchMock.mock.calls[0];
2929
assertHeaders(settings, headers);
30-
expect(url).toBe('auth/v2/auth?users=key1&users=key2');
30+
expect(url).toBe('auth/v2/auth?s=1.1&users=key1&users=key2');
3131

3232
splitApi.fetchMySegments('userKey');
3333
[url, { headers }] = fetchMock.mock.calls[1];
@@ -42,7 +42,7 @@ describe('splitApi', () => {
4242
splitApi.fetchSplitChanges(-1, false, 100);
4343
[url, { headers }] = fetchMock.mock.calls[3];
4444
assertHeaders(settings, headers);
45-
expect(url).toBe('sdk/splitChanges?since=-1&till=100');
45+
expect(url).toBe('sdk/splitChanges?s=1.1&since=-1&till=100');
4646

4747
splitApi.postEventsBulk('fake-body');
4848
assertHeaders(settings, fetchMock.mock.calls[4][1].headers);

src/services/splitApi.ts

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { splitHttpClientFactory } from './splitHttpClient';
44
import { ISplitApi } from './types';
55
import { objectAssign } from '../utils/lang/objectAssign';
66
import { ITelemetryTracker } from '../trackers/types';
7-
import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MY_SEGMENT } from '../utils/constants';
7+
import { SPLITS, IMPRESSIONS, IMPRESSIONS_COUNT, EVENTS, TELEMETRY, TOKEN, SEGMENT, MY_SEGMENT, FLAGS_SPEC } from '../utils/constants';
88
import { ERROR_TOO_MANY_SETS } from '../logger/constants';
99

1010
const noCacheHeaderOptions = { headers: { 'Cache-Control': 'no-cache' } };
@@ -44,17 +44,16 @@ export function splitApiFactory(
4444
},
4545

4646
fetchAuth(userMatchingKeys?: string[]) {
47-
let url = `${urls.auth}/v2/auth`;
48-
if (userMatchingKeys) { // accounting the possibility that `userMatchingKeys` is undefined (server-side API)
47+
let url = `${urls.auth}/v2/auth?s=${FLAGS_SPEC}`;
48+
if (userMatchingKeys) { // `userMatchingKeys` is undefined in server-side
4949
const queryParams = userMatchingKeys.map(userKeyToQueryParam).join('&');
50-
if (queryParams) // accounting the possibility that `userKeys` and thus `queryParams` are empty
51-
url += '?' + queryParams;
50+
if (queryParams) url += '&' + queryParams;
5251
}
5352
return splitHttpClient(url, undefined, telemetryTracker.trackHttp(TOKEN));
5453
},
5554

5655
fetchSplitChanges(since: number, noCache?: boolean, till?: number) {
57-
const url = `${urls.sdk}/splitChanges?since=${since}${till ? '&till=' + till : ''}${filterQueryString || ''}`;
56+
const url = `${urls.sdk}/splitChanges?s=${FLAGS_SPEC}&since=${since}${till ? '&till=' + till : ''}${filterQueryString || ''}`;
5857
return splitHttpClient(url, noCache ? noCacheHeaderOptions : undefined, telemetryTracker.trackHttp(SPLITS))
5958
.catch((err) => {
6059
if (err.statusCode === 414) settings.log.error(ERROR_TOO_MANY_SETS);

src/storages/KeyBuilder.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ISettings } from '../types';
2+
import { FLAGS_SPEC } from '../utils/constants';
23
import { startsWith } from '../utils/lang';
34
import { hash } from '../utils/murmur3/murmur3';
45

@@ -81,9 +82,9 @@ export class KeyBuilder {
8182
}
8283

8384
/**
84-
* Generates a murmur32 hash based on the authorization key and the feature flags filter query.
85+
* Generates a murmur32 hash based on the authorization key, the feature flags filter query, and version of SplitChanges API.
8586
* The hash is in hexadecimal format (8 characters max, 32 bits).
8687
*/
8788
export function getStorageHash(settings: ISettings) {
88-
return hash(`${settings.core.authorizationKey}::${settings.sync.__splitFiltersValidation.queryString}`).toString(16);
89+
return hash(`${settings.core.authorizationKey}::${settings.sync.__splitFiltersValidation.queryString}::${FLAGS_SPEC}`).toString(16);
8990
}

src/storages/__tests__/KeyBuilder.spec.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,15 +117,15 @@ test('getStorageHash', () => {
117117
expect(getStorageHash({
118118
core: { authorizationKey: '<fake-token-rfc>' },
119119
sync: { __splitFiltersValidation: { queryString: '&names=p1__split,p2__split' } }
120-
} as ISettings)).toBe('6a596ded');
120+
} as ISettings)).toBe('fdf7bd89');
121121

122122
expect(getStorageHash({
123123
core: { authorizationKey: '<fake-token-rfc>' },
124124
sync: { __splitFiltersValidation: { queryString: '&names=p2__split,p3__split' } }
125-
} as ISettings)).toBe('18ad36f3');
125+
} as ISettings)).toBe('ee4ec91');
126126

127127
expect(getStorageHash({
128128
core: { authorizationKey: '<fake-token-rfc>' },
129129
sync: { __splitFiltersValidation: { queryString: null } }
130-
} as ISettings)).toBe('9507ef4');
130+
} as ISettings)).toBe('2a2c20bb');
131131
});

src/sync/streaming/AuthClient/__tests__/index.spec.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ test('hashUserKey', () => {
2121

2222
test('authenticate / success in node (200)', done => {
2323

24-
fetchMock.getOnce(authUrl + '/v2/auth', (url, opts) => {
24+
fetchMock.getOnce(authUrl + '/v2/auth?s=1.1', (url, opts) => {
2525
// @ts-ignore
2626
expect(opts.headers['Authorization']).toBe(`Bearer ${authorizationKey}`); // auth request must contain Authorization header with config authorizationKey
2727
return { status: 200, body: authDataResponseSample };
@@ -40,7 +40,7 @@ test('authenticate / success in browser (200)', done => {
4040

4141
const userKeys = ['emi@split.io', 'maldo@split.io'];
4242

43-
fetchMock.getOnce(authUrl + '/v2/auth?users=emi%40split.io&users=maldo%40split.io', (url, opts) => {
43+
fetchMock.getOnce(authUrl + '/v2/auth?s=1.1&users=emi%40split.io&users=maldo%40split.io', (url, opts) => {
4444
// @ts-ignore
4545
expect(opts.headers['Authorization']).toBe(`Bearer ${authorizationKey}`); // auth request must contain Authorization header with config authorizationKey
4646
return { status: 200, body: authDataResponseSample };
@@ -57,7 +57,7 @@ test('authenticate / success in browser (200)', done => {
5757

5858
test('authenticate / bad request in browser due to no user keys (400)', done => {
5959

60-
fetchMock.getOnce(authUrl + '/v2/auth', { status: 400, body: '"no user specified"' });
60+
fetchMock.getOnce(authUrl + '/v2/auth?s=1.1', { status: 400, body: '"no user specified"' });
6161

6262
authenticate([]).then(() => {
6363
throw new Error('if bad request, promise is rejected');
@@ -70,7 +70,7 @@ test('authenticate / bad request in browser due to no user keys (400)', done =>
7070

7171
test('authenticate / Invalid credentials (401)', done => {
7272

73-
fetchMock.getOnce(authUrl + '/v2/auth', { status: 401, body: '"Invalid credentials"' });
73+
fetchMock.getOnce(authUrl + '/v2/auth?s=1.1', { status: 401, body: '"Invalid credentials"' });
7474

7575
authenticate([]).then(() => {
7676
throw new Error('if invalid credential, promise should be rejected');
@@ -85,7 +85,7 @@ test('authenticate / HTTP error (other than 401)', done => {
8585

8686
const NOT_OK_STATUS_CODE = 500;
8787

88-
fetchMock.getOnce(authUrl + '/v2/auth', { status: NOT_OK_STATUS_CODE, body: 'some error message' });
88+
fetchMock.getOnce(authUrl + '/v2/auth?s=1.1', { status: NOT_OK_STATUS_CODE, body: 'some error message' });
8989

9090
authenticate([]).then(() => {
9191
throw new Error('if an HTTP error, promise is rejected');
@@ -98,7 +98,7 @@ test('authenticate / HTTP error (other than 401)', done => {
9898

9999
test('authenticate / Network error (e.g., timeout)', done => {
100100

101-
fetchMock.getOnce(authUrl + '/v2/auth', { throws: new TypeError('Network error') });
101+
fetchMock.getOnce(authUrl + '/v2/auth?s=1.1', { throws: new TypeError('Network error') });
102102

103103
authenticate([]).then(() => {
104104
throw new Error('if network error, promise is rejected');
@@ -111,9 +111,9 @@ test('authenticate / Network error (e.g., timeout)', done => {
111111

112112
test('authenticate / Error parsing token', done => {
113113

114-
fetchMock.getOnce(authUrl + '/v2/auth', { status: 200, body: { pushEnabled: true, token: jwtSampleInvalid } });
115-
fetchMock.getOnce(authUrl + '/v2/auth', { status: 200, body: { pushEnabled: true, token: jwtSampleNoChannels } });
116-
fetchMock.getOnce(authUrl + '/v2/auth', { status: 200, body: { pushEnabled: true, token: jwtSampleNoIat } });
114+
fetchMock.getOnce(authUrl + '/v2/auth?s=1.1', { status: 200, body: { pushEnabled: true, token: jwtSampleInvalid } });
115+
fetchMock.getOnce(authUrl + '/v2/auth?s=1.1', { status: 200, body: { pushEnabled: true, token: jwtSampleNoChannels } });
116+
fetchMock.getOnce(authUrl + '/v2/auth?s=1.1', { status: 200, body: { pushEnabled: true, token: jwtSampleNoIat } });
117117

118118
authenticate().then(() => {
119119
throw new Error('if invalid token, promise is rejected');

src/utils/constants/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,5 @@ export const NON_REQUESTED = 1;
104104
export const DISABLED = 0;
105105
export const ENABLED = 1;
106106
export const PAUSED = 2;
107+
108+
export const FLAGS_SPEC = '1.1';

0 commit comments

Comments
 (0)