Skip to content

Commit 3bb6712

Browse files
Merge pull request #211 from splitio/development
Release 2.8.0
2 parents 14bfd95 + 41424fb commit 3bb6712

21 files changed

+979
-545
lines changed

CHANGES.txt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
1+
2.8.0 (Jul 23, 2025)
2+
- Updated base image to node:24.3.0-alpine3.22
3+
- Updated @splitsoftware/splitio package to version 11.4.1 that includes:
4+
- Added support for rule-based segments. These segments determine membership at runtime by evaluating their configured rules against the user attributes provided to the SDK.
5+
- Added support for feature flag prerequisites. This allows customers to define dependency conditions between flags, which are evaluated before any allowlists or targeting rules.
6+
- Added a new optional argument to the client `getTreatment` methods to allow passing additional evaluation options, such as a map of properties to append to the generated impressions sent to Split backend. Read more in our docs.
7+
- Added support for the new impressions tracking toggle available on feature flags, both respecting the setting and including the new field being returned on `SplitView` type objects. Read more in our docs.
8+
9+
110
2.7.2 (Jul 4, 2025)
211
- Updated base image to node:24.3.0-alpine3.21
312

413
2.7.1 (Jun 25, 2025)
5-
- Fixed OpenAPI spec fot /manager/splits
14+
- Fixed OpenAPI spec for /manager/splits
615
- Updated base image to node:24.2.0-alpine3.22
716

817
2.7.0 (Dec 20, 2024)

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Builder stage
2-
FROM node:24.3.0-alpine3.21 AS builder
2+
FROM node:24.3.0-alpine3.22 AS builder
33

44
WORKDIR /usr/src/split-evaluator
55

@@ -8,7 +8,7 @@ COPY package.json package-lock.json ./
88
RUN npm install --only=production
99

1010
# Runner stage
11-
FROM node:24.3.0-alpine3.21 AS runner
11+
FROM node:24.3.0-alpine3.22 AS runner
1212

1313
WORKDIR /usr/src/split-evaluator
1414

client/__tests__/allTreatments.test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,4 +259,38 @@ describe('get-all-treatments', () => {
259259
.set('Authorization', 'test');
260260
expectOkAllTreatments(response, 200, expected, 2);
261261
});
262+
263+
test('should be 200 if properties is valid (GET)', async () => {
264+
const response = await request(app)
265+
.get('/client/get-all-treatments?keys=[{"matchingKey":"test","trafficType":"localhost"}]&properties={"package":"premium","admin":true,"discount":50}')
266+
.set('Authorization', 'test');
267+
expect(response.status).toBe(200);
268+
});
269+
270+
test('should be 200 if properties is valid (POST)', async () => {
271+
const response = await request(app)
272+
.post('/client/get-all-treatments?keys=[{"matchingKey":"test","trafficType":"localhost"}]')
273+
.send({
274+
properties: { package: 'premium', admin: true, discount: 50 },
275+
})
276+
.set('Authorization', 'test');
277+
expect(response.status).toBe(200);
278+
});
279+
280+
test('should be 200 if properties is invalid (GET)', async () => {
281+
const response = await request(app)
282+
.get('/client/get-all-treatments?keys=[{"matchingKey":"test","trafficType":"localhost"}]&properties={"foo": {"bar": 1}}')
283+
.set('Authorization', 'test');
284+
expect(response.status).toBe(200);
285+
});
286+
287+
test('should be 200 if properties is invalid (POST)', async () => {
288+
const response = await request(app)
289+
.post('/client/get-all-treatments?keys=[{"matchingKey":"test","trafficType":"localhost"}]')
290+
.send({
291+
properties: { foo: { bar: 1 } },
292+
})
293+
.set('Authorization', 'test');
294+
expect(response.status).toBe(200);
295+
});
262296
});

client/__tests__/allTreatmentsWithConfig.test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,4 +267,38 @@ describe('get-all-treatments-with-config', () => {
267267
.set('Authorization', 'test');
268268
expectOkAllTreatments(response, 200, expected, 2);
269269
});
270+
271+
test('should be 200 if properties is valid (GET)', async () => {
272+
const response = await request(app)
273+
.get('/client/get-all-treatments-with-config?keys=[{"matchingKey":"test","trafficType":"localhost"}]&properties={"package":"premium","admin":true,"discount":50}')
274+
.set('Authorization', 'test');
275+
expect(response.status).toBe(200);
276+
});
277+
278+
test('should be 200 if properties is valid (POST)', async () => {
279+
const response = await request(app)
280+
.post('/client/get-all-treatments-with-config?keys=[{"matchingKey":"test","trafficType":"localhost"}]')
281+
.send({
282+
properties: { package: 'premium', admin: true, discount: 50 },
283+
})
284+
.set('Authorization', 'test');
285+
expect(response.status).toBe(200);
286+
});
287+
288+
test('should be 200 if properties is invalid (GET)', async () => {
289+
const response = await request(app)
290+
.get('/client/get-all-treatments-with-config?keys=[{"matchingKey":"test","trafficType":"localhost"}]&properties={"foo": {"bar": 1}}')
291+
.set('Authorization', 'test');
292+
expect(response.status).toBe(200);
293+
});
294+
295+
test('should be 200 if properties is invalid (POST)', async () => {
296+
const response = await request(app)
297+
.post('/client/get-all-treatments-with-config?keys=[{"matchingKey":"test","trafficType":"localhost"}]')
298+
.send({
299+
properties: { foo: { bar: 1 } },
300+
})
301+
.set('Authorization', 'test');
302+
expect(response.status).toBe(200);
303+
});
270304
});

client/__tests__/track.test.js

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ process.env.SPLIT_EVALUATOR_API_KEY = 'localhost';
44
const request = require('supertest');
55
const app = require('../../app');
66
const { expectError, expectErrorContaining, getLongKey } = require('../../utils/testWrapper');
7+
const { PROPERTIES_WARNING } = require('../../utils/constants');
78

89
describe('track', () => {
910
// Testing authorization
@@ -143,22 +144,21 @@ describe('track', () => {
143144
expectErrorContaining(response, 400, expected);
144145
});
145146

146-
test('should be 400 if properties is invalid', async () => {
147-
const expected = [
148-
'properties must be a plain object.'
149-
];
147+
test('should be 200 if properties is invalid', async () => {
148+
const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
150149
const response = await request(app)
151150
.get('/client/track?key=my-key&event-type=my-event&traffic-type=my-traffic&value=1&properties=lalala')
152151
.set('Authorization', 'test');
153-
expectErrorContaining(response, 400, expected);
152+
expect(response.statusCode).toBe(200);
153+
expect(logSpy).toHaveBeenCalledWith(PROPERTIES_WARNING);
154+
logSpy.mockRestore();
154155
});
155156

156157
test('should be 400 if there are multiple errors in every input', async () => {
157158
const expected = [
158159
'key too long, key must be 250 characters or less.',
159160
'you passed "@!test", event-type must adhere to the regular expression /^[a-zA-Z0-9][-_.:a-zA-Z0-9]{0,79}$/g. This means an event_type must be alphanumeric, cannot be more than 80 characters long, and can only include a dash, underscore, period, or colon as separators of alphanumeric characters.',
160161
'you passed an empty traffic-type, traffic-type must be a non-empty string.',
161-
'properties must be a plain object.',
162162
'value must be null or number.'
163163
];
164164
const key = getLongKey();

client/__tests__/treatment.test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,4 +247,38 @@ describe('get-treatment', () => {
247247
.set('Authorization', 'test');
248248
expectOk(response, 200, 'control', 'nonexistant-experiment');
249249
});
250+
251+
test('should be 200 if properties is valid (GET)', async () => {
252+
const response = await request(app)
253+
.get('/client/get-treatment?key=test&split-name=my-experiment&properties={"package":"premium","admin":true,"discount":50}')
254+
.set('Authorization', 'test');
255+
expectOk(response, 200, 'on', 'my-experiment');
256+
});
257+
258+
test('should be 200 if properties is valid (POST)', async () => {
259+
const response = await request(app)
260+
.post('/client/get-treatment?key=test&split-name=my-experiment')
261+
.send({
262+
properties: { package: 'premium', admin: true, discount: 50 },
263+
})
264+
.set('Authorization', 'test');
265+
expectOk(response, 200, 'on', 'my-experiment');
266+
});
267+
268+
test('should be 200 if properties is invalid (GET)', async () => {
269+
const response = await request(app)
270+
.get('/client/get-treatment?key=test&split-name=my-experiment&properties={"foo": {"bar": 1}}')
271+
.set('Authorization', 'test');
272+
expectOk(response, 200, 'on', 'my-experiment');
273+
});
274+
275+
test('should be 200 if properties is invalid (POST)', async () => {
276+
const response = await request(app)
277+
.post('/client/get-treatment?key=test&split-name=my-experiment')
278+
.send({
279+
properties: { foo: { bar: 1 } },
280+
})
281+
.set('Authorization', 'test');
282+
expectOk(response, 200, 'on', 'my-experiment');
283+
});
250284
});

client/__tests__/treatmentWithConfig.test.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,4 +229,38 @@ describe('get-treatment-with-config', () => {
229229
.set('Authorization', 'test');
230230
expectOk(response, 200, 'control', 'nonexistant-experiment', null);
231231
});
232+
233+
test('should be 200 if properties is valid (GET)', async () => {
234+
const response = await request(app)
235+
.get('/client/get-treatment-with-config?key=test&split-name=my-experiment&properties={"package":"premium","admin":true,"discount":50}')
236+
.set('Authorization', 'test');
237+
expectOk(response, 200, 'on', 'my-experiment', '{"desc" : "this applies only to ON treatment"}');
238+
});
239+
240+
test('should be 200 if properties is valid (POST)', async () => {
241+
const response = await request(app)
242+
.post('/client/get-treatment-with-config?key=test&split-name=my-experiment')
243+
.send({
244+
properties: { package: 'premium', admin: true, discount: 50 },
245+
})
246+
.set('Authorization', 'test');
247+
expectOk(response, 200, 'on', 'my-experiment', '{"desc" : "this applies only to ON treatment"}');
248+
});
249+
250+
test('should be 200 if properties is invalid (GET)', async () => {
251+
const response = await request(app)
252+
.get('/client/get-treatment-with-config?key=test&split-name=my-experiment&properties={"foo": {"bar": 1}}')
253+
.set('Authorization', 'test');
254+
expectOk(response, 200, 'on', 'my-experiment', '{"desc" : "this applies only to ON treatment"}');
255+
});
256+
257+
test('should be 200 if properties is invalid (POST)', async () => {
258+
const response = await request(app)
259+
.post('/client/get-treatment-with-config?key=test&split-name=my-experiment')
260+
.send({
261+
properties: { foo: { bar: 1 } },
262+
})
263+
.set('Authorization', 'test');
264+
expectOk(response, 200, 'on', 'my-experiment', '{"desc" : "this applies only to ON treatment"}');
265+
});
232266
});

client/__tests__/treatments.test.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,4 +267,54 @@ describe('get-treatments', () => {
267267
},
268268
}, 3);
269269
});
270+
271+
test('should be 200 if properties is valid (GET)', async () => {
272+
const response = await request(app)
273+
.get('/client/get-treatments?key=test&split-names=my-experiment&properties={"package":"premium","admin":true,"discount":50}')
274+
.set('Authorization', 'test');
275+
expectOkMultipleResults(response, 200, {
276+
'my-experiment': {
277+
treatment: 'on',
278+
},
279+
}, 1);
280+
});
281+
282+
test('should be 200 if properties is valid (POST)', async () => {
283+
const response = await request(app)
284+
.post('/client/get-treatments?key=test&split-names=my-experiment')
285+
.send({
286+
properties: { package: 'premium', admin: true, discount: 50 },
287+
})
288+
.set('Authorization', 'test');
289+
expectOkMultipleResults(response, 200, {
290+
'my-experiment': {
291+
treatment: 'on',
292+
},
293+
}, 1);
294+
});
295+
296+
test('should be 200 if properties is invalid (GET)', async () => {
297+
const response = await request(app)
298+
.get('/client/get-treatments?key=test&split-names=my-experiment&properties={"foo": {"bar": 1}}')
299+
.set('Authorization', 'test');
300+
expectOkMultipleResults(response, 200, {
301+
'my-experiment': {
302+
treatment: 'on',
303+
},
304+
}, 1);
305+
});
306+
307+
test('should be 200 if properties is invalid (POST)', async () => {
308+
const response = await request(app)
309+
.post('/client/get-treatments?key=test&split-names=my-experiment')
310+
.send({
311+
properties: { foo: { bar: 1 } },
312+
})
313+
.set('Authorization', 'test');
314+
expectOkMultipleResults(response, 200, {
315+
'my-experiment': {
316+
treatment: 'on',
317+
},
318+
}, 1);
319+
});
270320
});

client/__tests__/treatmentsByFlagSets.test.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const { expectedGreenResults, expectedPurpleResults, expectedPinkResults } = req
77
jest.mock('node-fetch', () => {
88
return jest.fn().mockImplementation((url) => {
99

10-
const sdkUrl = 'https://sdk.test.io/api/splitChanges?s=1.1&since=-1';
10+
const sdkUrl = 'https://sdk.test.io/api/splitChanges?s=1.3&since=-1';
1111
const splitChange2 = require('../../utils/mocks/splitchanges.since.-1.till.1602796638344.json');
1212
if (url.startsWith(sdkUrl)) return Promise.resolve({ status: 200, json: () => (splitChange2), ok: true });
1313

@@ -273,4 +273,38 @@ describe('get-treatments-by-sets', () => {
273273
.set('Authorization', 'key_pink');
274274
expectOkMultipleResults(response, 200, expectedPinkResults, 5);
275275
});
276+
277+
test('should be 200 if properties is valid (GET)', async () => {
278+
const response = await request(app)
279+
.get('/client/get-treatments-by-sets?key=test&flag-sets=set_green&properties={"package":"premium","admin":true,"discount":50}')
280+
.set('Authorization', 'key_green');
281+
expect(response.status).toBe(200);
282+
});
283+
284+
test('should be 200 if properties is valid (POST)', async () => {
285+
const response = await request(app)
286+
.post('/client/get-treatments-by-sets?key=test&flag-sets=set_green')
287+
.send({
288+
properties: { package: 'premium', admin: true, discount: 50 },
289+
})
290+
.set('Authorization', 'key_green');
291+
expect(response.status).toBe(200);
292+
});
293+
294+
test('should be 200 if properties is invalid (GET)', async () => {
295+
const response = await request(app)
296+
.get('/client/get-treatments-by-sets?key=test&flag-sets=set_green&properties={"foo": {"bar": 1}}')
297+
.set('Authorization', 'key_green');
298+
expect(response.status).toBe(200);
299+
});
300+
301+
test('should be 200 if properties is invalid (POST)', async () => {
302+
const response = await request(app)
303+
.post('/client/get-treatments-by-sets?key=test&flag-sets=set_green')
304+
.send({
305+
properties: { foo: { bar: 1 } },
306+
})
307+
.set('Authorization', 'key_green');
308+
expect(response.status).toBe(200);
309+
});
276310
});

client/__tests__/treatmentsWithConfig.test.js

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,4 +252,58 @@ describe('get-treatments-with-config', () => {
252252
},
253253
}, 3);
254254
});
255+
256+
test('should be 200 if properties is valid (GET)', async () => {
257+
const response = await request(app)
258+
.get('/client/get-treatments-with-config?key=test&split-names=my-experiment&properties={"package":"premium","admin":true,"discount":50}')
259+
.set('Authorization', 'test');
260+
expectOkMultipleResults(response, 200, {
261+
'my-experiment': {
262+
treatment: 'on',
263+
config: '{"desc" : "this applies only to ON treatment"}',
264+
},
265+
}, 1);
266+
});
267+
268+
test('should be 200 if properties is valid (POST)', async () => {
269+
const response = await request(app)
270+
.post('/client/get-treatments-with-config?key=test&split-names=my-experiment')
271+
.send({
272+
properties: { package: 'premium', admin: true, discount: 50 },
273+
})
274+
.set('Authorization', 'test');
275+
expectOkMultipleResults(response, 200, {
276+
'my-experiment': {
277+
treatment: 'on',
278+
config: '{"desc" : "this applies only to ON treatment"}',
279+
},
280+
}, 1);
281+
});
282+
283+
test('should be 200 if properties is invalid (GET)', async () => {
284+
const response = await request(app)
285+
.get('/client/get-treatments-with-config?key=test&split-names=my-experiment&properties={"foo": {"bar": 1}}')
286+
.set('Authorization', 'test');
287+
expectOkMultipleResults(response, 200, {
288+
'my-experiment': {
289+
treatment: 'on',
290+
config: '{"desc" : "this applies only to ON treatment"}',
291+
},
292+
}, 1);
293+
});
294+
295+
test('should be 200 if properties is invalid (POST)', async () => {
296+
const response = await request(app)
297+
.post('/client/get-treatments-with-config?key=test&split-names=my-experiment')
298+
.send({
299+
properties: { foo: { bar: 1 } },
300+
})
301+
.set('Authorization', 'test');
302+
expectOkMultipleResults(response, 200, {
303+
'my-experiment': {
304+
treatment: 'on',
305+
config: '{"desc" : "this applies only to ON treatment"}',
306+
},
307+
}, 1);
308+
});
255309
});

0 commit comments

Comments
 (0)