Skip to content

Commit c480ca3

Browse files
vpaiploomizpetzel
authored
Migrate Javascript SDK to use V2 Randomization Endpoint (#10)
* update dtos to match rac v2 structure - also add "-dto" suffix to dto file names - remove duplicate rules dto file * update getAssignment logic to account for v2 rac structure * get tests to pass following update to getAssigment logic * create dto and client folders, dismantle experiment folder also add comments to getAssignment function * address pr comments - matchesAnyRule -> findMatchingRule - remove redundant rule length check * Update src/dto/variation-dto.ts Co-authored-by: Eric Petzel <[email protected]> * 0.1.0 Co-authored-by: Peter Loomis <[email protected]> Co-authored-by: Eric Petzel <[email protected]> Co-authored-by: Peter Loomis <[email protected]>
1 parent 0b63bb9 commit c480ca3

16 files changed

+185
-222
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ testDataDir := test/data/
2929
test-data:
3030
rm -rf $(testDataDir)
3131
mkdir -p $(testDataDir)
32-
gsutil cp gs://sdk-test-data/rac-experiments.json $(testDataDir)
32+
gsutil cp gs://sdk-test-data/rac-experiments-v2.json $(testDataDir)
3333
gsutil cp -r gs://sdk-test-data/assignment-v2 $(testDataDir)
3434

3535
## prepare

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@eppo/js-client-sdk",
3-
"version": "0.0.7",
3+
"version": "0.1.0",
44
"description": "Eppo SDK for client-side JavaScript applications",
55
"main": "dist/index.js",
66
"files": [

src/eppo-client.spec.ts renamed to src/client/eppo-client.spec.ts

Lines changed: 75 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,19 @@
44
import * as td from 'testdouble';
55
import mock from 'xhr-mock';
66

7+
import { getInstance, init } from '..';
78
import {
89
IAssignmentTestCase,
910
readAssignmentTestData,
1011
readMockRacResponse,
11-
} from '../test/testHelpers';
12+
} from '../../test/testHelpers';
13+
import { IAssignmentLogger } from '../assignment-logger';
14+
import { MAX_EVENT_QUEUE_SIZE } from '../constants';
15+
import { OperatorType } from '../dto/rule-dto';
16+
import { EppoLocalStorage } from '../local-storage';
17+
import { EppoSessionStorage } from '../session-storage';
1218

13-
import { IAssignmentLogger } from './assignment-logger';
14-
import { MAX_EVENT_QUEUE_SIZE } from './constants';
1519
import EppoClient from './eppo-client';
16-
import { EppoLocalStorage } from './local-storage';
17-
import { OperatorType } from './rule';
18-
import { EppoSessionStorage } from './session-storage';
19-
20-
import { getInstance, init } from '.';
2120

2221
describe('EppoClient E2E test', () => {
2322
const sessionOverrideSubject = 'subject-14';
@@ -27,16 +26,14 @@ describe('EppoClient E2E test', () => {
2726
window.localStorage.clear();
2827
window.sessionStorage.clear();
2928
mock.setup();
30-
mock.get(/randomized_assignment\/config*/, (_req, res) => {
29+
mock.get(/randomized_assignment\/v2\/config*/, (_req, res) => {
3130
const rac = readMockRacResponse();
3231
return res.status(200).body(JSON.stringify(rac));
3332
});
3433
const preloadedConfig = {
3534
name: preloadedConfigExperiment,
36-
percentExposure: 1,
3735
enabled: true,
3836
subjectShards: 100,
39-
variations: mockVariations,
4037
overrides: {
4138
'5160f8b1a59fb002f8535257206cb824': 'preloaded-config-variation',
4239
},
@@ -56,50 +53,61 @@ describe('EppoClient E2E test', () => {
5653
mock.teardown();
5754
});
5855

59-
const mockVariations = [
60-
{
61-
name: 'control',
62-
shardRange: {
63-
start: 0,
64-
end: 34,
65-
},
66-
},
67-
{
68-
name: 'variant-1',
69-
shardRange: {
70-
start: 34,
71-
end: 67,
56+
const experimentName = 'mock-experiment';
57+
58+
const mockExperimentConfig = {
59+
name: experimentName,
60+
enabled: true,
61+
subjectShards: 100,
62+
overrides: {},
63+
rules: [
64+
{
65+
allocationKey: 'allocation1',
66+
conditions: [],
7267
},
73-
},
74-
{
75-
name: 'variant-2',
76-
shardRange: {
77-
start: 67,
78-
end: 100,
68+
],
69+
allocations: {
70+
allocation1: {
71+
percentExposure: 1,
72+
variations: [
73+
{
74+
name: 'control',
75+
value: 'control',
76+
shardRange: {
77+
start: 0,
78+
end: 34,
79+
},
80+
},
81+
{
82+
name: 'variant-1',
83+
value: 'variant-1',
84+
shardRange: {
85+
start: 34,
86+
end: 67,
87+
},
88+
},
89+
{
90+
name: 'variant-2',
91+
value: 'variant-2',
92+
shardRange: {
93+
start: 67,
94+
end: 100,
95+
},
96+
},
97+
],
7998
},
8099
},
81-
];
100+
};
82101

83102
describe('setLogger', () => {
84-
const experiment = 'exp-111';
85103
beforeAll(() => {
86-
window.localStorage.setItem(
87-
experiment,
88-
JSON.stringify({
89-
name: experiment,
90-
percentExposure: 1,
91-
enabled: true,
92-
subjectShards: 100,
93-
variations: mockVariations,
94-
overrides: {},
95-
}),
96-
);
104+
window.localStorage.setItem(experimentName, JSON.stringify(mockExperimentConfig));
97105
});
98106

99107
it('Invokes logger for queued events', () => {
100108
const mockLogger = td.object<IAssignmentLogger>();
101109
const client = new EppoClient(new EppoLocalStorage(), new EppoSessionStorage());
102-
client.getAssignment('subject-to-be-logged', experiment);
110+
client.getAssignment('subject-to-be-logged', experimentName);
103111
client.setLogger(mockLogger);
104112
expect(td.explain(mockLogger.logAssignment).callCount).toEqual(1);
105113
expect(td.explain(mockLogger.logAssignment).calls[0].args[0].subject).toEqual(
@@ -110,7 +118,7 @@ describe('EppoClient E2E test', () => {
110118
it('Does not log same queued event twice', () => {
111119
const mockLogger = td.object<IAssignmentLogger>();
112120
const client = new EppoClient(new EppoLocalStorage(), new EppoSessionStorage());
113-
client.getAssignment('subject-to-be-logged', experiment);
121+
client.getAssignment('subject-to-be-logged', experimentName);
114122
client.setLogger(mockLogger);
115123
expect(td.explain(mockLogger.logAssignment).callCount).toEqual(1);
116124
client.setLogger(mockLogger);
@@ -121,7 +129,7 @@ describe('EppoClient E2E test', () => {
121129
const mockLogger = td.object<IAssignmentLogger>();
122130
const client = new EppoClient(new EppoLocalStorage(), new EppoSessionStorage());
123131
for (let i = 0; i < MAX_EVENT_QUEUE_SIZE + 100; i++) {
124-
client.getAssignment(`subject-to-be-logged-${i}`, experiment);
132+
client.getAssignment(`subject-to-be-logged-${i}`, experimentName);
125133
}
126134
client.setLogger(mockLogger);
127135
expect(td.explain(mockLogger.logAssignment).callCount).toEqual(MAX_EVENT_QUEUE_SIZE);
@@ -153,103 +161,67 @@ describe('EppoClient E2E test', () => {
153161
});
154162

155163
it('returns subject from overrides when enabled is true', () => {
156-
const experiment = 'experiment_5';
157164
window.localStorage.setItem(
158-
experiment,
165+
experimentName,
159166
JSON.stringify({
160-
name: experiment,
161-
percentExposure: 1,
162-
enabled: true,
163-
subjectShards: 100,
164-
variations: mockVariations,
167+
...mockExperimentConfig,
165168
overrides: {
166-
a90ea45116d251a43da56e03d3dd7275: 'variant-2',
169+
'1b50f33aef8f681a13f623963da967ed': 'control',
167170
},
168171
}),
169172
);
170173
const client = new EppoClient(new EppoLocalStorage(), new EppoSessionStorage());
171-
const assignment = client.getAssignment('subject-1', experiment);
172-
expect(assignment).toEqual('variant-2');
174+
const assignment = client.getAssignment('subject-10', experimentName);
175+
expect(assignment).toEqual('control');
173176
});
174177

175178
it('returns subject from overrides when enabled is false', () => {
176-
const experiment = 'experiment_5';
177179
window.localStorage.setItem(
178-
experiment,
180+
experimentName,
179181
JSON.stringify({
180-
name: experiment,
181-
percentExposure: 0,
182+
...mockExperimentConfig,
182183
enabled: false,
183-
subjectShards: 100,
184-
variations: mockVariations,
185184
overrides: {
186-
a90ea45116d251a43da56e03d3dd7275: 'variant-2',
185+
'1b50f33aef8f681a13f623963da967ed': 'control',
187186
},
188187
}),
189188
);
190189
const client = new EppoClient(new EppoLocalStorage(), new EppoSessionStorage());
191-
const assignment = client.getAssignment('subject-1', experiment);
192-
expect(assignment).toEqual('variant-2');
190+
const assignment = client.getAssignment('subject-10', experimentName);
191+
expect(assignment).toEqual('control');
193192
});
194193

195194
it('logs variation assignment', () => {
196195
const mockLogger = td.object<IAssignmentLogger>();
197-
const experiment = 'experiment_5';
198-
window.localStorage.setItem(
199-
experiment,
200-
JSON.stringify({
201-
name: experiment,
202-
percentExposure: 1,
203-
enabled: true,
204-
subjectShards: 100,
205-
variations: mockVariations,
206-
overrides: {},
207-
}),
208-
);
196+
window.localStorage.setItem(experimentName, JSON.stringify(mockExperimentConfig));
209197
const subjectAttributes = { foo: 3 };
210198
const client = new EppoClient(new EppoLocalStorage(), new EppoSessionStorage());
211199
client.setLogger(mockLogger);
212-
const assignment = client.getAssignment('subject-1', experiment, subjectAttributes);
200+
const assignment = client.getAssignment('subject-10', experimentName, subjectAttributes);
213201
expect(assignment).toEqual('control');
214202
expect(td.explain(mockLogger.logAssignment).callCount).toEqual(1);
215-
expect(td.explain(mockLogger.logAssignment).calls[0].args[0].subject).toEqual('subject-1');
203+
expect(td.explain(mockLogger.logAssignment).calls[0].args[0].subject).toEqual('subject-10');
216204
});
217205

218206
it('handles logging exception', () => {
219207
const mockLogger = td.object<IAssignmentLogger>();
220-
const experiment = 'experiment_5';
221208
td.when(mockLogger.logAssignment(td.matchers.anything())).thenThrow(new Error('logging error'));
222-
window.localStorage.setItem(
223-
experiment,
224-
JSON.stringify({
225-
name: experiment,
226-
percentExposure: 1,
227-
enabled: true,
228-
subjectShards: 100,
229-
variations: mockVariations,
230-
overrides: {},
231-
}),
232-
);
209+
window.localStorage.setItem(experimentName, JSON.stringify(mockExperimentConfig));
233210
const subjectAttributes = { foo: 3 };
234211
const client = new EppoClient(new EppoLocalStorage(), new EppoSessionStorage());
235212
client.setLogger(mockLogger);
236-
const assignment = client.getAssignment('subject-1', experiment, subjectAttributes);
213+
const assignment = client.getAssignment('subject-10', experimentName, subjectAttributes);
237214
expect(assignment).toEqual('control');
238215
});
239216

240217
it('only returns variation if subject matches rules', () => {
241-
const experiment = 'experiment_5';
242218
window.localStorage.setItem(
243-
experiment,
219+
experimentName,
244220
JSON.stringify({
245-
name: experiment,
246-
percentExposure: 1,
247-
enabled: true,
248-
subjectShards: 100,
249-
variations: mockVariations,
250-
overrides: {},
221+
...mockExperimentConfig,
251222
rules: [
252223
{
224+
allocationKey: 'allocation1',
253225
conditions: [
254226
{
255227
operator: OperatorType.GT,
@@ -262,11 +234,11 @@ describe('EppoClient E2E test', () => {
262234
}),
263235
);
264236
const client = new EppoClient(new EppoLocalStorage(), new EppoSessionStorage());
265-
let assignment = client.getAssignment('subject-1', experiment, { appVersion: 9 });
237+
let assignment = client.getAssignment('subject-10', experimentName, { appVersion: 9 });
266238
expect(assignment).toEqual(null);
267-
assignment = client.getAssignment('subject-1', experiment);
239+
assignment = client.getAssignment('subject-10', experimentName);
268240
expect(assignment).toEqual(null);
269-
assignment = client.getAssignment('subject-1', experiment, { appVersion: 11 });
241+
assignment = client.getAssignment('subject-10', experimentName, { appVersion: 11 });
270242
expect(assignment).toEqual('control');
271243
});
272244

0 commit comments

Comments
 (0)