Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
42cf24d
Update tests to validate expirationDays config
EmilianoSanchez Dec 19, 2024
2dd7820
Add test to validate clearOnInit config
EmilianoSanchez Dec 19, 2024
d97a03f
Prepare rc and update changelog entry
EmilianoSanchez Dec 19, 2024
7c4fc2d
Polishing
EmilianoSanchez Dec 20, 2024
0ab2752
Updated SDK_READY_FROM_CACHE event when using the `LOCALSTORAGE` stor…
EmilianoSanchez Jan 2, 2025
957cbdc
rc
EmilianoSanchez Jan 3, 2025
e6f768f
Merge branch 'master' into cache_expiration
EmilianoSanchez Jan 13, 2025
5cd064a
Merge branch 'cache_expiration' into SDKS-9171_sdk_ready_from_cache
EmilianoSanchez Jan 13, 2025
2bb8006
Merge branch 'SDKS-9171_sdk_ready_from_cache' into release_v11.1.0
EmilianoSanchez Jan 13, 2025
b463726
Fix tests
EmilianoSanchez Jan 13, 2025
aafd396
rc
EmilianoSanchez Jan 13, 2025
833ba1d
Fix test
EmilianoSanchez Jan 13, 2025
75df407
rollback ci-cd
EmilianoSanchez Jan 13, 2025
d072826
Merge branch 'master' into cache_expiration
EmilianoSanchez Jan 17, 2025
e19bb21
Merge branch 'cache_expiration' into SDKS-9171_sdk_ready_from_cache
EmilianoSanchez Jan 17, 2025
7f04526
Merge pull request #854 from splitio/SDKS-9171_sdk_ready_from_cache
EmilianoSanchez Jan 17, 2025
4097277
Deps vulnerability fixes, README update and CI-CD image update
EmilianoSanchez Feb 28, 2025
b8c0910
Update Karma configuration to use a custom ChromeHeadless launcher fo…
EmilianoSanchez Feb 28, 2025
e2dc151
Merge pull request #860 from splitio/misc
EmilianoSanchez Feb 28, 2025
afb1ae8
Rename some variables in test suite for readability
EmilianoSanchez Mar 26, 2025
5e2559c
Merge branch 'development' into misc
EmilianoSanchez Mar 26, 2025
768a2ea
Merge pull request #864 from splitio/misc
EmilianoSanchez Mar 26, 2025
303a7ec
E2E tests for standalone mode
EmilianoSanchez Mar 27, 2025
158aafa
E2E tests for consumer mode
EmilianoSanchez Mar 27, 2025
aa0f7ca
rc
EmilianoSanchez Mar 27, 2025
4c6821b
upload CDN rc
EmilianoSanchez Mar 27, 2025
8d08048
Revert "upload CDN rc"
EmilianoSanchez Mar 28, 2025
8c215fc
Merge pull request #865 from splitio/impression_properties
EmilianoSanchez Mar 28, 2025
e113b7e
Merge branch 'development' into cache_expiration
EmilianoSanchez Mar 28, 2025
8eb73ab
rc
EmilianoSanchez Mar 28, 2025
8ea9d82
rc
EmilianoSanchez Mar 28, 2025
4466a55
stable version
EmilianoSanchez Mar 28, 2025
01eca47
Merge pull request #852 from splitio/cache_expiration
EmilianoSanchez Mar 28, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .github/workflows/ci-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,7 @@ permissions:
jobs:
build:
name: Build
# @TODO rollback to ubuntu-latest eventually. ATM, `npm run test-browser` fails when using ubuntu-latest (ubuntu-22.04) with "ERROR [launcher]: Cannot start ChromeHeadless"
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/sonar-scan.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ on:
jobs:
build:
name: Build
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/update-license-year.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ permissions:

jobs:
test:
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
Expand Down
7 changes: 7 additions & 0 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
11.2.0 (March 28, 2025)
- 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.
- Added two new configuration options for the SDK's `LOCALSTORAGE` storage type to control the behavior of the persisted rollout plan cache in the browser:
- `storage.expirationDays` to specify the validity period of the rollout plan cache in days.
- `storage.clearOnInit` to clear the rollout plan cache on SDK initialization.
- Updated SDK_READY_FROM_CACHE event when using the `LOCALSTORAGE` storage type to be emitted alongside the SDK_READY event if it has not already been emitted.

11.1.0 (January 17, 2025)
- 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.
- Updated @splitsoftware/splitio-commons package to version 2.1.0.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ Split has built and maintains SDKs for:
* .NET [Github](https://github.com/splitio/dotnet-client) [Docs](https://help.split.io/hc/en-us/articles/360020240172--NET-SDK)
* Android [Github](https://github.com/splitio/android-client) [Docs](https://help.split.io/hc/en-us/articles/360020343291-Android-SDK)
* Angular [Github](https://github.com/splitio/angular-sdk-plugin) [Docs](https://help.split.io/hc/en-us/articles/6495326064397-Angular-utilities)
* Elixir thin-client [Github](https://github.com/splitio/elixir-thin-client) [Docs](https://help.split.io/hc/en-us/articles/26988707417869-Elixir-Thin-Client-SDK)
* Flutter [Github](https://github.com/splitio/flutter-sdk-plugin) [Docs](https://help.split.io/hc/en-us/articles/8096158017165-Flutter-plugin)
* GO [Github](https://github.com/splitio/go-client) [Docs](https://help.split.io/hc/en-us/articles/360020093652-Go-SDK)
* iOS [Github](https://github.com/splitio/ios-client) [Docs](https://help.split.io/hc/en-us/articles/360020401491-iOS-SDK)
Expand Down
11 changes: 8 additions & 3 deletions karma/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,14 @@ module.exports = {
],

// Run on Chrome Headless
browsers: [
'ChromeHeadless'
],
customLaunchers: {
ChromeHeadlessNoSandbox: {
base: 'ChromeHeadless',
// Flags required to run in ubuntu-22.04 or above (https://chromium.googlesource.com/chromium/src/+/master/docs/linux/suid_sandbox_development.md)
flags: ['--no-sandbox', '--disable-setuid-sandbox']
}
},
browsers: ['ChromeHeadlessNoSandbox'],

// list of files / patterns to load in the browser
files: [
Expand Down
30 changes: 15 additions & 15 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@splitsoftware/splitio",
"version": "11.1.0",
"version": "11.2.0",
"description": "Split SDK",
"files": [
"README.md",
Expand Down Expand Up @@ -38,7 +38,7 @@
"node": ">=14.0.0"
},
"dependencies": {
"@splitsoftware/splitio-commons": "2.1.0",
"@splitsoftware/splitio-commons": "2.2.0",
"bloom-filters": "^3.0.4",
"ioredis": "^4.28.0",
"js-yaml": "^3.13.1",
Expand Down
7 changes: 4 additions & 3 deletions src/__tests__/browserSuites/impressions-listener.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default function (assert) {
const testAttrs = { is_test: true };

// Impression listener is shared across all client instances and does not get affected by configurations.
client.getTreatment('hierarchical_splits_test');
client.getTreatment('hierarchical_splits_test', undefined, { properties: { prop1: 'prop-value' } });
client2.getTreatment('qc_team');
client2.getTreatmentWithConfig('qc_team'); // Validate that the impression is the same.
client3.getTreatment('qc_team', testAttrs);
Expand All @@ -58,7 +58,8 @@ export default function (assert) {
treatment: 'no',
bucketingKey: 'impr_bucketing_2',
label: 'default rule',
pt: undefined
pt: undefined,
properties: undefined
};

assert.equal(listener.logImpression.callCount, 4, 'Impression listener logImpression method should be called after we call client.getTreatment, once per each impression generated.');
Expand All @@ -71,7 +72,7 @@ export default function (assert) {
bucketingKey: undefined,
label: 'expected label',
changeNumber: 2828282828,
pt: undefined
properties: '{"prop1":"prop-value"}'
},
attributes: undefined,
...metaData
Expand Down
20 changes: 17 additions & 3 deletions src/__tests__/browserSuites/impressions.debug.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,14 @@ export default function (fetchMock, assert) {
k: 'facundo@split.io', t: 'o.n', m: data[0].i[1].m, c: 828282828282, r: 'another expected label', pt: data[0].i[0].m,
}, {
k: 'facundo@split.io', t: 'o.n', m: data[0].i[2].m, c: 828282828282, r: 'another expected label', pt: data[0].i[1].m
}, {
k: 'facundo@split.io', t: 'o.n', m: data[0].i[3].m, c: 828282828282, r: 'another expected label', properties: '{"prop1":"value1"}'
}, {
k: 'facundo@split.io', t: 'o.n', m: data[0].i[4].m, c: 828282828282, r: 'another expected label', properties: '{"prop1":"value2"}'
}, {
k: 'facundo@split.io', t: 'o.n', m: data[0].i[5].m, c: 828282828282, r: 'another expected label', properties: '{"prop1":"value3"}'
}, {
k: 'facundo@split.io', t: 'o.n', m: data[0].i[6].m, c: 828282828282, r: 'another expected label', properties: '{"prop1":"value4"}'
}]
}]);

Expand All @@ -75,15 +83,15 @@ export default function (fetchMock, assert) {

fetchMock.postOnce(url(settings, '/testImpressions/count'), (url, opts) => {
assert.deepEqual(JSON.parse(opts.body), {
pf: [{ f: 'always_on_track_impressions_false', m: truncatedTimeFrame, rc: 1 }]
pf: [{ f: 'always_on_impressions_disabled_true', m: truncatedTimeFrame, rc: 1 }]
}, 'We should generate impression count for the feature with track impressions disabled.');

return 200;
});

fetchMock.postOnce(url(settings, '/v1/keys/cs'), (url, opts) => {
assert.deepEqual(JSON.parse(opts.body), {
keys: [{ fs: ['always_on_track_impressions_false'], k: 'facundo@split.io' }]
keys: [{ fs: ['always_on_impressions_disabled_true'], k: 'facundo@split.io' }]
}, 'We should track unique keys for the feature with track impressions disabled.');

return 200;
Expand All @@ -95,6 +103,12 @@ export default function (fetchMock, assert) {
client.getTreatment('split_with_config');
client.getTreatment('split_with_config');
client.getTreatment('split_with_config');
assert.equal(client.getTreatment('always_on_track_impressions_false'), 'on');
assert.equal(client.getTreatment('always_on_impressions_disabled_true'), 'on');

// with properties
assert.equal(client.getTreatment('split_with_config', undefined, { properties: { prop1: 'value1' } }), 'o.n');
assert.equal(client.getTreatments(['split_with_config'], undefined, { properties: { prop1: 'value2' } }).split_with_config, 'o.n');
assert.equal(client.getTreatmentWithConfig('split_with_config', undefined, { properties: { prop1: 'value3' } }).treatment, 'o.n');
assert.equal(client.getTreatmentsWithConfig(['split_with_config'], undefined, { properties: { prop1: 'value4' } }).split_with_config.treatment, 'o.n');
});
}
6 changes: 3 additions & 3 deletions src/__tests__/browserSuites/impressions.none.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default async function (fetchMock, assert) {
{ f: 'split_with_config', m: truncatedTimeFrame, rc: 2 },
{ f: 'always_off', m: truncatedTimeFrame, rc: 4 },
{ f: 'always_on', m: truncatedTimeFrame, rc: 2 },
{ f: 'always_on_track_impressions_false', m: truncatedTimeFrame, rc: 1 }
{ f: 'always_on_impressions_disabled_true', m: truncatedTimeFrame, rc: 1 }
]
});
return 200;
Expand All @@ -76,7 +76,7 @@ export default async function (fetchMock, assert) {
},
{
k: 'emma@split.io',
fs: ['always_off', 'always_on', 'always_on_track_impressions_false']
fs: ['always_off', 'always_on', 'always_on_impressions_disabled_true']
}
]
}, 'We performed evaluations for two keys, so we should have 2 item total.');
Expand All @@ -94,7 +94,7 @@ export default async function (fetchMock, assert) {
client.getTreatment('always_on');
client.getTreatment('always_off');
client.getTreatment('split_with_config');
sharedClient.getTreatment('always_on_track_impressions_false');
sharedClient.getTreatment('always_on_impressions_disabled_true');

client.destroy().then(() => {
assert.end();
Expand Down
75 changes: 31 additions & 44 deletions src/__tests__/browserSuites/impressions.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,32 +47,21 @@ export default function (fetchMock, assert) {

const client = splitio.client();
const assertPayload = req => {
const resp = JSON.parse(req.body);

assert.equal(resp.length, 2, 'We performed evaluations for 3 features, but one with `impressionsDisabled` true, so we should have 2 items total');

const dependencyChildImpr = resp.filter(e => e.f === 'hierarchical_splits_test')[0];
const splitWithConfigImpr = resp.filter(e => e.f === 'split_with_config')[0];
const alwaysOnWithTrackImpressionsFalse = resp.filter(e => e.f === 'always_on_track_impressions_false');

assert.true(dependencyChildImpr, 'Split we wanted to evaluate should be present on the impressions.');
assert.false(resp.some(e => e.f === 'hierarchical_dep_always_on'), 'Parent split evaluations should not result in impressions.');
assert.false(resp.some(e => e.f === 'hierarchical_dep_hierarchical'), 'No matter how deep is the chain.');
assert.true(splitWithConfigImpr, 'Split evaluated with config should have generated an impression too.');
assert.false(Object.prototype.hasOwnProperty.call(splitWithConfigImpr.i[0], 'configuration'), 'Impressions do not change with configuration evaluations.');
assert.false(Object.prototype.hasOwnProperty.call(splitWithConfigImpr.i[0], 'config'), 'Impressions do not change with configuration evaluations.');
assert.equal(alwaysOnWithTrackImpressionsFalse.length, 0);

const {
k,
r,
t
} = dependencyChildImpr.i[0];

assert.equal(k, 'facundo@split.io', 'Present impression should have the correct key.');
// The label present on the mock.
assert.equal(r, 'expected label', 'Present impression should have the correct label.');
assert.equal(t, 'on', 'Present impression should have the correct treatment.');
const reqBody = JSON.parse(req.body);

assert.deepEqual(reqBody, [{
f: 'hierarchical_splits_test',
i: [{
k: 'facundo@split.io', t: 'on', m: reqBody[0].i[0].m, c: 2828282828, r: 'expected label'
}]
}, {
f: 'split_with_config',
i: [{
k: 'facundo@split.io', t: 'o.n', m: reqBody[1].i[0].m, c: 828282828282, r: 'another expected label'
}, {
k: 'facundo@split.io', t: 'o.n', m: reqBody[1].i[1].m, c: 828282828282, r: 'another expected label', properties: '{"some":"value2"}'
}]
}], 'We performed evaluations for 3 features, but one with `impressionsDisabled` true, so we should have 2 items total');
};

fetchMock.postOnce(url(settings, '/testImpressions/bulk'), (url, req) => {
Expand All @@ -93,28 +82,23 @@ export default function (fetchMock, assert) {
return 200;
});

fetchMock.postOnce(url(settings, '/testImpressions/count'), (url, opts) => {
const data = JSON.parse(opts.body);
fetchMock.postOnce(url(settings, '/testImpressions/count'), (url, req) => {
const reqBody = JSON.parse(req.body);

assert.equal(data.pf.length, 2, 'We should generate impressions count for 2 features.');

// finding these validate the feature names collection too
const splitWithConfigImpr = data.pf.filter(e => e.f === 'split_with_config')[0];
const alwaysOnWithTrackImpressionsFalse = data.pf.filter(e => e.f === 'always_on_track_impressions_false')[0];

assert.equal(splitWithConfigImpr.rc, 2);
assert.equal(typeof splitWithConfigImpr.m, 'number');
assert.equal(splitWithConfigImpr.m, truncatedTimeFrame);
assert.equal(alwaysOnWithTrackImpressionsFalse.rc, 1);
assert.equal(typeof alwaysOnWithTrackImpressionsFalse.m, 'number');
assert.equal(alwaysOnWithTrackImpressionsFalse.m, truncatedTimeFrame);
assert.deepEqual(reqBody, {
pf: [{
f: 'split_with_config', m: truncatedTimeFrame, rc: 2
}, {
f: 'always_on_impressions_disabled_true', m: truncatedTimeFrame, rc: 1
}]
}, 'We should generate impressions count for 2 features.');

return 200;
});

fetchMock.postOnce(url(settings, '/v1/keys/cs'), (url, opts) => {
assert.deepEqual(JSON.parse(opts.body), {
keys: [{ fs: [ 'always_on_track_impressions_false' ], k: 'facundo@split.io' }]
keys: [{ fs: ['always_on_impressions_disabled_true'], k: 'facundo@split.io' }]
}, 'We should only track unique keys for features flags with track impressions disabled.');

return 200;
Expand All @@ -129,9 +113,12 @@ export default function (fetchMock, assert) {
config: '{"color":"brown","dimensions":{"height":12,"width":14},"text":{"inner":"click me"}}'
}, 'We should get an evaluation as always.');
client.getTreatmentWithConfig('split_with_config');
client.getTreatmentWithConfig('split_with_config');
client.getTreatmentWithConfig('split_with_config', undefined, { properties: { /* empty properties are ignored */ } });

// Impression should not be tracked (passed properties will not be submitted)
assert.equal(client.getTreatment('always_on_impressions_disabled_true'), 'on', undefined, { properties: { some: 'value1' } });

// Impression should not be tracked
assert.equal(client.getTreatment('always_on_track_impressions_false'), 'on');
// Tracked impression with properties should be handled in DEBUG mode (doesn't increase `rc` count but adds an impression)
assert.equal(client.getTreatment('split_with_config', undefined, { properties: { some: 'value2' } }), 'o.n');
});
}
Loading
Loading