@naturalcycles/abba 1.7.0 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/abba.d.ts +15 -29
- package/dist/abba.js +56 -107
- package/dist/dao/bucket.dao.d.ts +5 -0
- package/dist/dao/bucket.dao.js +15 -0
- package/dist/dao/experiment.dao.d.ts +10 -0
- package/dist/dao/experiment.dao.js +19 -0
- package/dist/dao/userAssignment.dao.d.ts +5 -0
- package/dist/dao/userAssignment.dao.js +15 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +1 -1
- package/dist/migrations/init.sql +47 -0
- package/dist/types.d.ts +30 -7
- package/dist/util.d.ts +5 -5
- package/package.json +7 -8
- package/readme.md +8 -9
- package/src/abba.ts +90 -134
- package/src/dao/bucket.dao.ts +13 -0
- package/src/dao/experiment.dao.ts +22 -0
- package/src/dao/userAssignment.dao.ts +13 -0
- package/src/index.ts +0 -3
- package/src/migrations/init.sql +47 -0
- package/src/types.ts +33 -7
- package/src/util.ts +5 -5
- package/dist/prisma-output/index-browser.js +0 -141
- package/dist/prisma-output/index.d.ts +0 -5526
- package/dist/prisma-output/index.js +0 -217
- package/dist/prisma-output/libquery_engine-darwin-arm64.dylib.node +0 -0
- package/dist/prisma-output/libquery_engine-darwin.dylib.node +0 -0
- package/dist/prisma-output/libquery_engine-debian-openssl-1.1.x.so.node +0 -0
- package/dist/prisma-output/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
- package/dist/prisma-output/runtime/esm/index-browser.mjs +0 -2370
- package/dist/prisma-output/runtime/esm/index.mjs +0 -40587
- package/dist/prisma-output/runtime/esm/proxy.mjs +0 -113
- package/dist/prisma-output/runtime/index-browser.d.ts +0 -269
- package/dist/prisma-output/runtime/index-browser.js +0 -2621
- package/dist/prisma-output/runtime/index.d.ts +0 -1384
- package/dist/prisma-output/runtime/index.js +0 -59183
- package/dist/prisma-output/runtime/proxy.d.ts +0 -1384
- package/dist/prisma-output/runtime/proxy.js +0 -13576
- package/dist/prisma-output/schema.prisma +0 -47
- package/src/prisma-output/index-browser.js +0 -141
- package/src/prisma-output/index.d.ts +0 -5526
- package/src/prisma-output/index.js +0 -217
- package/src/prisma-output/libquery_engine-darwin-arm64.dylib.node +0 -0
- package/src/prisma-output/libquery_engine-darwin.dylib.node +0 -0
- package/src/prisma-output/libquery_engine-debian-openssl-1.1.x.so.node +0 -0
- package/src/prisma-output/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
- package/src/prisma-output/runtime/esm/index-browser.mjs +0 -2370
- package/src/prisma-output/runtime/esm/index.mjs +0 -40587
- package/src/prisma-output/runtime/esm/proxy.mjs +0 -113
- package/src/prisma-output/runtime/index-browser.d.ts +0 -269
- package/src/prisma-output/runtime/index-browser.js +0 -2621
- package/src/prisma-output/runtime/index.d.ts +0 -1384
- package/src/prisma-output/runtime/index.js +0 -59183
- package/src/prisma-output/runtime/proxy.d.ts +0 -1384
- package/src/prisma-output/runtime/proxy.js +0 -13576
- package/src/prisma-output/schema.prisma +0 -47
package/dist/abba.d.ts
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Saved } from '@naturalcycles/js-lib';
|
|
2
|
+
import { AbbaConfig, Bucket, Experiment, ExperimentWithBuckets, UserAssignment } from './types';
|
|
3
|
+
import { SegmentationData, AssignmentStatistics } from '.';
|
|
3
4
|
export declare class Abba {
|
|
4
|
-
private
|
|
5
|
-
|
|
5
|
+
private experimentDao;
|
|
6
|
+
private bucketDao;
|
|
7
|
+
private userAssignmentDao;
|
|
8
|
+
constructor({ db }: AbbaConfig);
|
|
6
9
|
/**
|
|
7
10
|
* Returns all experiments
|
|
8
11
|
*
|
|
@@ -16,7 +19,7 @@ export declare class Abba {
|
|
|
16
19
|
* @param buckets
|
|
17
20
|
* @returns
|
|
18
21
|
*/
|
|
19
|
-
createExperiment(experiment:
|
|
22
|
+
createExperiment(experiment: Experiment, buckets: Bucket[]): Promise<ExperimentWithBuckets>;
|
|
20
23
|
/**
|
|
21
24
|
* Update experiment information, will also validate the buckets ratio if experiment.active is true
|
|
22
25
|
*
|
|
@@ -26,7 +29,7 @@ export declare class Abba {
|
|
|
26
29
|
* @param buckets
|
|
27
30
|
* @returns
|
|
28
31
|
*/
|
|
29
|
-
saveExperiment(id: number, experiment:
|
|
32
|
+
saveExperiment(id: number, experiment: Experiment, buckets: Bucket[]): Promise<ExperimentWithBuckets>;
|
|
30
33
|
/**
|
|
31
34
|
* Delete an experiment. Removes all user assignments and buckets
|
|
32
35
|
*
|
|
@@ -38,18 +41,18 @@ export declare class Abba {
|
|
|
38
41
|
*
|
|
39
42
|
* @param experimentId
|
|
40
43
|
* @param userId
|
|
41
|
-
* @param
|
|
42
|
-
* @param segmentationData
|
|
44
|
+
* @param existingOnly Do not generate any new assignments for this experiment
|
|
45
|
+
* @param segmentationData Required if existingOnly is false
|
|
43
46
|
* @returns
|
|
44
47
|
*/
|
|
45
|
-
getUserAssignment(experimentId: number, userId: string, existingOnly: boolean, segmentationData?: SegmentationData): Promise<UserAssignment | null>;
|
|
48
|
+
getUserAssignment(experimentId: number, userId: string, existingOnly: boolean, segmentationData?: SegmentationData): Promise<Saved<UserAssignment> | null>;
|
|
46
49
|
/**
|
|
47
50
|
* Get all existing user assignments
|
|
48
51
|
*
|
|
49
|
-
* @param userId
|
|
52
|
+
* @param userId
|
|
50
53
|
* @returns
|
|
51
54
|
*/
|
|
52
|
-
getAllExistingUserAssignments(userId: string): Promise<UserAssignment[]>;
|
|
55
|
+
getAllExistingUserAssignments(userId: string): Promise<Saved<UserAssignment>[]>;
|
|
53
56
|
/**
|
|
54
57
|
* Generate user assignments for all active experiments. Will return any existing and attempt to generate any new assignments.
|
|
55
58
|
*
|
|
@@ -57,7 +60,7 @@ export declare class Abba {
|
|
|
57
60
|
* @param segmentationData
|
|
58
61
|
* @returns
|
|
59
62
|
*/
|
|
60
|
-
generateUserAssignments(userId: string, segmentationData: SegmentationData): Promise<UserAssignment[]>;
|
|
63
|
+
generateUserAssignments(userId: string, segmentationData: SegmentationData): Promise<Saved<UserAssignment>[]>;
|
|
61
64
|
/**
|
|
62
65
|
* Get assignment statistics for an experiment
|
|
63
66
|
*
|
|
@@ -82,21 +85,4 @@ export declare class Abba {
|
|
|
82
85
|
* @returns
|
|
83
86
|
*/
|
|
84
87
|
private getExistingUserAssignment;
|
|
85
|
-
/**
|
|
86
|
-
* Update experiment information
|
|
87
|
-
*
|
|
88
|
-
* @param id
|
|
89
|
-
* @param experiment
|
|
90
|
-
* @param rules
|
|
91
|
-
* @returns
|
|
92
|
-
*/
|
|
93
|
-
private updateExperiment;
|
|
94
|
-
/**
|
|
95
|
-
* Upserts bucket info
|
|
96
|
-
*
|
|
97
|
-
* @param id
|
|
98
|
-
* @param experiment
|
|
99
|
-
* @returns
|
|
100
|
-
*/
|
|
101
|
-
private saveBuckets;
|
|
102
88
|
}
|
package/dist/abba.js
CHANGED
|
@@ -1,32 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Abba = void 0;
|
|
4
|
-
const
|
|
4
|
+
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
5
5
|
const types_1 = require("./types");
|
|
6
6
|
const util_1 = require("./util");
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
const experiment_dao_1 = require("./dao/experiment.dao");
|
|
8
|
+
const userAssignment_dao_1 = require("./dao/userAssignment.dao");
|
|
9
|
+
const bucket_dao_1 = require("./dao/bucket.dao");
|
|
10
10
|
class Abba {
|
|
11
|
-
constructor(
|
|
12
|
-
this.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
url: dbUrl,
|
|
16
|
-
},
|
|
17
|
-
},
|
|
18
|
-
});
|
|
11
|
+
constructor({ db }) {
|
|
12
|
+
this.experimentDao = (0, experiment_dao_1.experimentDao)(db);
|
|
13
|
+
this.bucketDao = (0, bucket_dao_1.bucketDao)(db);
|
|
14
|
+
this.userAssignmentDao = (0, userAssignment_dao_1.userAssignmentDao)(db);
|
|
19
15
|
}
|
|
16
|
+
// TODO: Cache me
|
|
20
17
|
/**
|
|
21
18
|
* Returns all experiments
|
|
22
19
|
*
|
|
23
20
|
* @returns
|
|
24
21
|
*/
|
|
25
22
|
async getAllExperiments(excludeInactive = false) {
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
}
|
|
23
|
+
const query = this.experimentDao.query();
|
|
24
|
+
if (excludeInactive) {
|
|
25
|
+
query.filter('status', '!=', types_1.AssignmentStatus.Inactive);
|
|
26
|
+
}
|
|
27
|
+
const experiments = await this.experimentDao.runQuery(query);
|
|
28
|
+
const buckets = await this.bucketDao
|
|
29
|
+
.query()
|
|
30
|
+
.filter('experimentId', 'in', experiments.map(e => e.id))
|
|
31
|
+
.runQuery();
|
|
32
|
+
return experiments.map(experiment => ({
|
|
33
|
+
...experiment,
|
|
34
|
+
buckets: buckets.filter(bucket => bucket.experimentId === experiment.id),
|
|
35
|
+
}));
|
|
30
36
|
}
|
|
31
37
|
/**
|
|
32
38
|
* Creates a new experiment
|
|
@@ -39,21 +45,11 @@ class Abba {
|
|
|
39
45
|
if (experiment.status === types_1.AssignmentStatus.Active) {
|
|
40
46
|
(0, util_1.validateTotalBucketRatio)(buckets);
|
|
41
47
|
}
|
|
42
|
-
const created = await this.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
createMany: {
|
|
48
|
-
data: buckets,
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
include: {
|
|
53
|
-
buckets: true,
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
return created;
|
|
48
|
+
const created = await this.experimentDao.save(experiment);
|
|
49
|
+
return {
|
|
50
|
+
...created,
|
|
51
|
+
buckets: await this.bucketDao.saveBatch(buckets.map(b => ({ ...b, experimentId: created.id }))),
|
|
52
|
+
};
|
|
57
53
|
}
|
|
58
54
|
/**
|
|
59
55
|
* Update experiment information, will also validate the buckets ratio if experiment.active is true
|
|
@@ -68,11 +64,13 @@ class Abba {
|
|
|
68
64
|
if (experiment.status === types_1.AssignmentStatus.Active) {
|
|
69
65
|
(0, util_1.validateTotalBucketRatio)(buckets);
|
|
70
66
|
}
|
|
71
|
-
const
|
|
72
|
-
|
|
67
|
+
const updated = await this.experimentDao.save({
|
|
68
|
+
...experiment,
|
|
69
|
+
id,
|
|
70
|
+
});
|
|
73
71
|
return {
|
|
74
|
-
...
|
|
75
|
-
buckets:
|
|
72
|
+
...updated,
|
|
73
|
+
buckets: await this.bucketDao.saveBatch(buckets.map(b => ({ ...b, experimentId: id }))),
|
|
76
74
|
};
|
|
77
75
|
}
|
|
78
76
|
/**
|
|
@@ -81,24 +79,22 @@ class Abba {
|
|
|
81
79
|
* @param id
|
|
82
80
|
*/
|
|
83
81
|
async deleteExperiment(id) {
|
|
84
|
-
await this.
|
|
82
|
+
await this.experimentDao.deleteById(id);
|
|
85
83
|
}
|
|
86
84
|
/**
|
|
87
85
|
* Get an assignment for a given user. If existingOnly is false, it will attempt generate a new assignment
|
|
88
86
|
*
|
|
89
87
|
* @param experimentId
|
|
90
88
|
* @param userId
|
|
91
|
-
* @param
|
|
92
|
-
* @param segmentationData
|
|
89
|
+
* @param existingOnly Do not generate any new assignments for this experiment
|
|
90
|
+
* @param segmentationData Required if existingOnly is false
|
|
93
91
|
* @returns
|
|
94
92
|
*/
|
|
95
93
|
async getUserAssignment(experimentId, userId, existingOnly, segmentationData) {
|
|
96
|
-
const experiment = await this.
|
|
97
|
-
where: { id: experimentId },
|
|
98
|
-
include: { buckets: true },
|
|
99
|
-
});
|
|
94
|
+
const experiment = await this.experimentDao.requireById(experimentId);
|
|
100
95
|
if (!experiment)
|
|
101
96
|
throw new Error('Experiment not found');
|
|
97
|
+
const buckets = await this.bucketDao.getBy('experimentId', experimentId);
|
|
102
98
|
const existing = await this.getExistingUserAssignment(experimentId, userId);
|
|
103
99
|
if (existing)
|
|
104
100
|
return existing;
|
|
@@ -106,16 +102,16 @@ class Abba {
|
|
|
106
102
|
return null;
|
|
107
103
|
if (!segmentationData)
|
|
108
104
|
throw new Error('Segmentation data required when creating a new assignment');
|
|
109
|
-
return await this.generateUserAssignment(experiment, userId, segmentationData);
|
|
105
|
+
return await this.generateUserAssignment({ ...experiment, buckets }, userId, segmentationData);
|
|
110
106
|
}
|
|
111
107
|
/**
|
|
112
108
|
* Get all existing user assignments
|
|
113
109
|
*
|
|
114
|
-
* @param userId
|
|
110
|
+
* @param userId
|
|
115
111
|
* @returns
|
|
116
112
|
*/
|
|
117
113
|
async getAllExistingUserAssignments(userId) {
|
|
118
|
-
return await this.
|
|
114
|
+
return await this.userAssignmentDao.getBy('userId', userId);
|
|
119
115
|
}
|
|
120
116
|
/**
|
|
121
117
|
* Generate user assignments for all active experiments. Will return any existing and attempt to generate any new assignments.
|
|
@@ -151,19 +147,16 @@ class Abba {
|
|
|
151
147
|
*/
|
|
152
148
|
async getExperimentAssignmentStatistics(experimentId) {
|
|
153
149
|
const statistics = {
|
|
154
|
-
sampled: await this.
|
|
150
|
+
sampled: await this.userAssignmentDao
|
|
151
|
+
.query()
|
|
152
|
+
.filterEq('experimentId', experimentId)
|
|
153
|
+
.runQueryCount(),
|
|
155
154
|
buckets: {},
|
|
156
155
|
};
|
|
157
|
-
const buckets = await this.
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
_count: {
|
|
162
|
-
_all: true,
|
|
163
|
-
},
|
|
164
|
-
});
|
|
165
|
-
buckets.forEach(({ id }) => {
|
|
166
|
-
statistics.buckets[`${id}`] = assignmentCounts.find(i => i.bucketId === id)?._count?._all || 0;
|
|
156
|
+
const buckets = await this.bucketDao.getBy('experimentId', experimentId);
|
|
157
|
+
await (0, js_lib_1.pMap)(buckets, async (bucket) => {
|
|
158
|
+
const count = this.userAssignmentDao.query().filterEq('bucketId', bucket.id);
|
|
159
|
+
statistics[bucket.id] = await this.userAssignmentDao.runQueryCount(count);
|
|
167
160
|
});
|
|
168
161
|
return statistics;
|
|
169
162
|
}
|
|
@@ -179,9 +172,10 @@ class Abba {
|
|
|
179
172
|
const segmentationMatch = (0, util_1.validateSegmentationRules)(experiment.rules, segmentationData);
|
|
180
173
|
if (!segmentationMatch)
|
|
181
174
|
return null;
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
175
|
+
return await this.userAssignmentDao.save({
|
|
176
|
+
userId,
|
|
177
|
+
experimentId: experiment.id,
|
|
178
|
+
bucketId: (0, util_1.determineAssignment)(experiment.sampling, experiment.buckets),
|
|
185
179
|
});
|
|
186
180
|
}
|
|
187
181
|
/**
|
|
@@ -192,54 +186,9 @@ class Abba {
|
|
|
192
186
|
* @returns
|
|
193
187
|
*/
|
|
194
188
|
async getExistingUserAssignment(experimentId, userId) {
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
* Update experiment information
|
|
199
|
-
*
|
|
200
|
-
* @param id
|
|
201
|
-
* @param experiment
|
|
202
|
-
* @param rules
|
|
203
|
-
* @returns
|
|
204
|
-
*/
|
|
205
|
-
async updateExperiment(id, experiment) {
|
|
206
|
-
return await this.client.experiment.update({
|
|
207
|
-
where: { id },
|
|
208
|
-
data: {
|
|
209
|
-
...experiment,
|
|
210
|
-
rules: experiment.rules,
|
|
211
|
-
},
|
|
212
|
-
});
|
|
213
|
-
}
|
|
214
|
-
/**
|
|
215
|
-
* Upserts bucket info
|
|
216
|
-
*
|
|
217
|
-
* @param id
|
|
218
|
-
* @param experiment
|
|
219
|
-
* @returns
|
|
220
|
-
*/
|
|
221
|
-
async saveBuckets(buckets) {
|
|
222
|
-
const savedBuckets = [];
|
|
223
|
-
for (const bucket of buckets) {
|
|
224
|
-
const { id, ...data } = bucket;
|
|
225
|
-
if (id) {
|
|
226
|
-
savedBuckets.push(this.client.bucket.update({ where: { id }, data }));
|
|
227
|
-
}
|
|
228
|
-
else {
|
|
229
|
-
savedBuckets.push(this.client.bucket.create({
|
|
230
|
-
data: {
|
|
231
|
-
key: data.key,
|
|
232
|
-
ratio: data.ratio,
|
|
233
|
-
experiment: {
|
|
234
|
-
connect: {
|
|
235
|
-
id: data.experimentId,
|
|
236
|
-
},
|
|
237
|
-
},
|
|
238
|
-
},
|
|
239
|
-
}));
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
return await Promise.all(savedBuckets);
|
|
189
|
+
const assignments = await this.userAssignmentDao.getBy('userId', userId);
|
|
190
|
+
const assignment = assignments.find(assignment => assignment.experimentId === experimentId);
|
|
191
|
+
return assignment || null;
|
|
243
192
|
}
|
|
244
193
|
}
|
|
245
194
|
exports.Abba = Abba;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.bucketDao = exports.BucketDao = void 0;
|
|
4
|
+
const db_lib_1 = require("@naturalcycles/db-lib");
|
|
5
|
+
class BucketDao extends db_lib_1.CommonDao {
|
|
6
|
+
}
|
|
7
|
+
exports.BucketDao = BucketDao;
|
|
8
|
+
const bucketDao = (db) => new BucketDao({
|
|
9
|
+
db,
|
|
10
|
+
table: 'Bucket',
|
|
11
|
+
createId: false,
|
|
12
|
+
idType: 'number',
|
|
13
|
+
assignGeneratedIds: true,
|
|
14
|
+
});
|
|
15
|
+
exports.bucketDao = bucketDao;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { CommonDao, CommonDB } from '@naturalcycles/db-lib';
|
|
2
|
+
import { Saved } from '@naturalcycles/js-lib';
|
|
3
|
+
import { BaseExperiment, Experiment } from '../types';
|
|
4
|
+
declare type ExperimentDBM = Saved<BaseExperiment> & {
|
|
5
|
+
rules: string | null;
|
|
6
|
+
};
|
|
7
|
+
export declare class ExperimentDao extends CommonDao<Experiment, ExperimentDBM> {
|
|
8
|
+
}
|
|
9
|
+
export declare const experimentDao: (db: CommonDB) => ExperimentDao;
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.experimentDao = exports.ExperimentDao = void 0;
|
|
4
|
+
const db_lib_1 = require("@naturalcycles/db-lib");
|
|
5
|
+
class ExperimentDao extends db_lib_1.CommonDao {
|
|
6
|
+
}
|
|
7
|
+
exports.ExperimentDao = ExperimentDao;
|
|
8
|
+
const experimentDao = (db) => new ExperimentDao({
|
|
9
|
+
db,
|
|
10
|
+
table: 'Experiment',
|
|
11
|
+
createId: false,
|
|
12
|
+
idType: 'number',
|
|
13
|
+
assignGeneratedIds: true,
|
|
14
|
+
hooks: {
|
|
15
|
+
beforeBMToDBM: bm => ({ ...bm, rules: bm.rules.length ? JSON.stringify(bm.rules) : null }),
|
|
16
|
+
beforeDBMToBM: dbm => ({ ...dbm, rules: dbm.rules && JSON.parse(dbm.rules) }),
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
exports.experimentDao = experimentDao;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.userAssignmentDao = exports.UserAssignmentDao = void 0;
|
|
4
|
+
const db_lib_1 = require("@naturalcycles/db-lib");
|
|
5
|
+
class UserAssignmentDao extends db_lib_1.CommonDao {
|
|
6
|
+
}
|
|
7
|
+
exports.UserAssignmentDao = UserAssignmentDao;
|
|
8
|
+
const userAssignmentDao = (db) => new UserAssignmentDao({
|
|
9
|
+
db,
|
|
10
|
+
table: 'UserAssignment',
|
|
11
|
+
createId: false,
|
|
12
|
+
idType: 'number',
|
|
13
|
+
assignGeneratedIds: true,
|
|
14
|
+
});
|
|
15
|
+
exports.userAssignmentDao = userAssignmentDao;
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -2,6 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Abba = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
|
-
|
|
5
|
+
tslib_1.__exportStar(require("./types"), exports);
|
|
6
6
|
var abba_1 = require("./abba");
|
|
7
7
|
Object.defineProperty(exports, "Abba", { enumerable: true, get: function () { return abba_1.Abba; } });
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
-- CreateTable
|
|
2
|
+
CREATE TABLE IF NOT EXISTS `Bucket` (
|
|
3
|
+
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
|
4
|
+
`experimentId` INTEGER NOT NULL,
|
|
5
|
+
`key` VARCHAR(10) NOT NULL,
|
|
6
|
+
`ratio` INTEGER NOT NULL,
|
|
7
|
+
`created` INTEGER(11) NOT NULL,
|
|
8
|
+
`updated` INTEGER(11) NOT NULL,
|
|
9
|
+
|
|
10
|
+
PRIMARY KEY (`id`)
|
|
11
|
+
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
12
|
+
|
|
13
|
+
-- CreateTable
|
|
14
|
+
CREATE TABLE IF NOT EXISTS `Experiment` (
|
|
15
|
+
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
|
16
|
+
`name` VARCHAR(191) NOT NULL,
|
|
17
|
+
`status` INTEGER NOT NULL,
|
|
18
|
+
`sampling` INTEGER NOT NULL,
|
|
19
|
+
`description` VARCHAR(240) NULL,
|
|
20
|
+
`created` INTEGER(11) NOT NULL,
|
|
21
|
+
`updated` INTEGER(11) NOT NULL,
|
|
22
|
+
`rules` JSON NULL,
|
|
23
|
+
|
|
24
|
+
PRIMARY KEY (`id`)
|
|
25
|
+
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
26
|
+
|
|
27
|
+
-- CreateTable
|
|
28
|
+
CREATE TABLE IF NOT EXISTS `UserAssignment` (
|
|
29
|
+
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
|
30
|
+
`userId` VARCHAR(191) NOT NULL,
|
|
31
|
+
`experimentId` INTEGER NOT NULL,
|
|
32
|
+
`bucketId` INTEGER NULL,
|
|
33
|
+
`created` INTEGER(11) NOT NULL,
|
|
34
|
+
`updated` INTEGER(11) NOT NULL,
|
|
35
|
+
|
|
36
|
+
UNIQUE INDEX `UserAssignment_userId_experimentId_key`(`userId`, `experimentId`),
|
|
37
|
+
PRIMARY KEY (`id`)
|
|
38
|
+
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
|
39
|
+
|
|
40
|
+
-- AddForeignKey
|
|
41
|
+
ALTER TABLE `Bucket` ADD CONSTRAINT `Bucket_experimentId_fkey` FOREIGN KEY (`experimentId`) REFERENCES `Experiment`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
42
|
+
|
|
43
|
+
-- AddForeignKey
|
|
44
|
+
ALTER TABLE `UserAssignment` ADD CONSTRAINT `UserAssignment_bucketId_fkey` FOREIGN KEY (`bucketId`) REFERENCES `Bucket`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
|
45
|
+
|
|
46
|
+
-- AddForeignKey
|
|
47
|
+
ALTER TABLE `UserAssignment` ADD CONSTRAINT `UserAssignment_experimentId_fkey` FOREIGN KEY (`experimentId`) REFERENCES `Experiment`(`id`) ON DELETE CASCADE ON UPDATE CASCADE;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,12 +1,35 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { CommonDB } from '@naturalcycles/db-lib';
|
|
2
|
+
import { BaseDBEntity, Saved } from '@naturalcycles/js-lib';
|
|
3
|
+
export interface AbbaConfig {
|
|
4
|
+
db: CommonDB;
|
|
5
|
+
}
|
|
6
|
+
export declare type BaseExperiment = BaseDBEntity<number> & {
|
|
7
|
+
name: string;
|
|
8
|
+
status: number;
|
|
9
|
+
sampling: number;
|
|
10
|
+
description: string | null;
|
|
11
|
+
};
|
|
12
|
+
export declare type Experiment = BaseExperiment & {
|
|
13
|
+
rules: SegmentationRule[];
|
|
14
|
+
};
|
|
15
|
+
export declare type ExperimentWithBuckets = Saved<Experiment> & {
|
|
16
|
+
buckets: Saved<Bucket>[];
|
|
17
|
+
};
|
|
18
|
+
export interface BucketInput {
|
|
19
|
+
experimentId: number;
|
|
20
|
+
key: string;
|
|
21
|
+
ratio: number;
|
|
22
|
+
}
|
|
23
|
+
export declare type Bucket = BaseDBEntity<number> & {
|
|
24
|
+
experimentId: number;
|
|
25
|
+
key: string;
|
|
26
|
+
ratio: number;
|
|
4
27
|
};
|
|
5
|
-
export declare type
|
|
6
|
-
|
|
28
|
+
export declare type UserAssignment = BaseDBEntity<number> & {
|
|
29
|
+
userId: string;
|
|
30
|
+
experimentId: number;
|
|
31
|
+
bucketId: number | null;
|
|
7
32
|
};
|
|
8
|
-
export declare type ExperimentInput = Unsaved<Experiment>;
|
|
9
|
-
export declare type BucketInput = Unsaved<Bucket>;
|
|
10
33
|
export declare type SegmentationData = Record<string, string | boolean | number>;
|
|
11
34
|
export declare enum AssignmentStatus {
|
|
12
35
|
Active = 1,
|
package/dist/util.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Saved } from '@naturalcycles/js-lib';
|
|
2
|
+
import { Bucket, SegmentationData, SegmentationRule } from './types';
|
|
3
3
|
/**
|
|
4
4
|
* Generate a random number between 0 and 100
|
|
5
5
|
*
|
|
@@ -13,21 +13,21 @@ export declare const rollDie: () => number;
|
|
|
13
13
|
* @param buckets
|
|
14
14
|
* @returns
|
|
15
15
|
*/
|
|
16
|
-
export declare const determineAssignment: (sampling: number, buckets: Bucket[]) => number | null;
|
|
16
|
+
export declare const determineAssignment: (sampling: number, buckets: Saved<Bucket>[]) => number | null;
|
|
17
17
|
/**
|
|
18
18
|
* Determines which bucket a user assignment will recieve
|
|
19
19
|
*
|
|
20
20
|
* @param buckets
|
|
21
21
|
* @returns
|
|
22
22
|
*/
|
|
23
|
-
export declare const determineBucket: (buckets: Bucket[]) => number;
|
|
23
|
+
export declare const determineBucket: (buckets: Saved<Bucket>[]) => number;
|
|
24
24
|
/**
|
|
25
25
|
* Validate the total ratio of the buckets equals 100
|
|
26
26
|
*
|
|
27
27
|
* @param buckets
|
|
28
28
|
* @returns
|
|
29
29
|
*/
|
|
30
|
-
export declare const validateTotalBucketRatio: (buckets:
|
|
30
|
+
export declare const validateTotalBucketRatio: (buckets: Bucket[]) => void;
|
|
31
31
|
/**
|
|
32
32
|
* Validate a users segmentation data against multiple rules. Returns false if any fail
|
|
33
33
|
*
|
package/package.json
CHANGED
|
@@ -1,22 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/abba",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.0",
|
|
4
4
|
"scripts": {
|
|
5
5
|
"prepare": "husky install",
|
|
6
|
-
"build": "build
|
|
7
|
-
"build-prod": "build-prod
|
|
8
|
-
"copy-prisma-output": "cp -R src/prisma-output dist"
|
|
6
|
+
"build": "build",
|
|
7
|
+
"build-prod": "build-prod"
|
|
9
8
|
},
|
|
10
9
|
"dependencies": {
|
|
11
|
-
"@
|
|
10
|
+
"@naturalcycles/db-lib": "^8.40.1",
|
|
11
|
+
"@naturalcycles/js-lib": "^14.98.2",
|
|
12
12
|
"semver": "^7.3.5"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
|
-
"@naturalcycles/dev-lib": "^12.
|
|
15
|
+
"@naturalcycles/dev-lib": "^12.19.2",
|
|
16
16
|
"@types/node": "^16.0.0",
|
|
17
17
|
"@types/semver": "^7.3.9",
|
|
18
|
-
"jest": "^27.5.1"
|
|
19
|
-
"prisma": "^3.13.0"
|
|
18
|
+
"jest": "^27.5.1"
|
|
20
19
|
},
|
|
21
20
|
"files": [
|
|
22
21
|
"dist",
|
package/readme.md
CHANGED
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
|
|
45
45
|
### Built With
|
|
46
46
|
|
|
47
|
-
- [
|
|
47
|
+
- [@naturalcycles/db-lib](https://github.com/NaturalCycles/db-lib)
|
|
48
48
|
|
|
49
49
|
<p align="right">(<a href="#top">back to top</a>)</p>
|
|
50
50
|
|
|
@@ -77,9 +77,8 @@ This template doesn't rely on any external dependencies or services._
|
|
|
77
77
|
npm install @naturalcyles/abba
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
2.
|
|
81
|
-
[
|
|
82
|
-
to generate the required DB Schema
|
|
80
|
+
2. Install the schema into your MySQL db instance using the migration script found
|
|
81
|
+
[here](https://github.com/NaturalCycles/abba/blob/master/src/migrations/init.sql).
|
|
83
82
|
|
|
84
83
|
<p align="right">(<a href="#top">back to top</a>)</p>
|
|
85
84
|
|
|
@@ -109,7 +108,7 @@ Creates a new experiment
|
|
|
109
108
|
async createExperiment(
|
|
110
109
|
input: ExperimentInput,
|
|
111
110
|
buckets: BucketInput[]
|
|
112
|
-
): Promise<Experiment
|
|
111
|
+
): Promise<Saved<Experiment>>
|
|
113
112
|
```
|
|
114
113
|
|
|
115
114
|
### Update an experiment
|
|
@@ -121,7 +120,7 @@ async updateExperiment(
|
|
|
121
120
|
id: number,
|
|
122
121
|
input: ExperimentInput,
|
|
123
122
|
buckets: BucketInput[]
|
|
124
|
-
): Promise<Experiment
|
|
123
|
+
): Promise<Saved<Experiment>>
|
|
125
124
|
```
|
|
126
125
|
|
|
127
126
|
### Delete an experiment
|
|
@@ -141,7 +140,7 @@ Gets all existing user assignments
|
|
|
141
140
|
```js
|
|
142
141
|
async getAllExistingUserAssignments(
|
|
143
142
|
userId: string
|
|
144
|
-
): Promise<UserAssignment[]>
|
|
143
|
+
): Promise<Saved<UserAssignment>[]>
|
|
145
144
|
```
|
|
146
145
|
|
|
147
146
|
### Get a users assignment
|
|
@@ -155,7 +154,7 @@ async getUserAssignment(
|
|
|
155
154
|
userId: string,
|
|
156
155
|
existingOnly: boolean,
|
|
157
156
|
segmentationData?: SegmentationData,
|
|
158
|
-
): Promise<UserAssignment | null>
|
|
157
|
+
): Promise<Saved<UserAssignment> | null>
|
|
159
158
|
```
|
|
160
159
|
|
|
161
160
|
### Generate user assignments
|
|
@@ -167,7 +166,7 @@ attempt to generate new assignments.
|
|
|
167
166
|
async generateUserAssignments(
|
|
168
167
|
userId: string,
|
|
169
168
|
segmentationData: SegmentationData,
|
|
170
|
-
): Promise<UserAssignment[]>
|
|
169
|
+
): Promise<Saved<UserAssignment>[]>
|
|
171
170
|
```
|
|
172
171
|
|
|
173
172
|
### Getting assignment statistics
|