@naturalcycles/abba 1.7.0 → 1.9.1

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.
Files changed (58) hide show
  1. package/dist/abba.d.ts +45 -72
  2. package/dist/abba.js +119 -165
  3. package/dist/dao/bucket.dao.d.ts +5 -0
  4. package/dist/dao/bucket.dao.js +15 -0
  5. package/dist/dao/experiment.dao.d.ts +10 -0
  6. package/dist/dao/experiment.dao.js +19 -0
  7. package/dist/dao/userAssignment.dao.d.ts +5 -0
  8. package/dist/dao/userAssignment.dao.js +15 -0
  9. package/dist/index.d.ts +0 -1
  10. package/dist/index.js +1 -1
  11. package/dist/migrations/init.sql +47 -0
  12. package/dist/types.d.ts +30 -7
  13. package/dist/util.d.ts +5 -21
  14. package/dist/util.js +0 -16
  15. package/package.json +9 -9
  16. package/readme.md +14 -15
  17. package/src/abba.ts +160 -191
  18. package/src/dao/bucket.dao.ts +13 -0
  19. package/src/dao/experiment.dao.ts +22 -0
  20. package/src/dao/userAssignment.dao.ts +13 -0
  21. package/src/index.ts +0 -3
  22. package/src/migrations/init.sql +47 -0
  23. package/src/types.ts +41 -7
  24. package/src/util.ts +5 -21
  25. package/dist/prisma-output/index-browser.js +0 -141
  26. package/dist/prisma-output/index.d.ts +0 -5526
  27. package/dist/prisma-output/index.js +0 -217
  28. package/dist/prisma-output/libquery_engine-darwin-arm64.dylib.node +0 -0
  29. package/dist/prisma-output/libquery_engine-darwin.dylib.node +0 -0
  30. package/dist/prisma-output/libquery_engine-debian-openssl-1.1.x.so.node +0 -0
  31. package/dist/prisma-output/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  32. package/dist/prisma-output/runtime/esm/index-browser.mjs +0 -2370
  33. package/dist/prisma-output/runtime/esm/index.mjs +0 -40587
  34. package/dist/prisma-output/runtime/esm/proxy.mjs +0 -113
  35. package/dist/prisma-output/runtime/index-browser.d.ts +0 -269
  36. package/dist/prisma-output/runtime/index-browser.js +0 -2621
  37. package/dist/prisma-output/runtime/index.d.ts +0 -1384
  38. package/dist/prisma-output/runtime/index.js +0 -59183
  39. package/dist/prisma-output/runtime/proxy.d.ts +0 -1384
  40. package/dist/prisma-output/runtime/proxy.js +0 -13576
  41. package/dist/prisma-output/schema.prisma +0 -47
  42. package/src/prisma-output/index-browser.js +0 -141
  43. package/src/prisma-output/index.d.ts +0 -5526
  44. package/src/prisma-output/index.js +0 -217
  45. package/src/prisma-output/libquery_engine-darwin-arm64.dylib.node +0 -0
  46. package/src/prisma-output/libquery_engine-darwin.dylib.node +0 -0
  47. package/src/prisma-output/libquery_engine-debian-openssl-1.1.x.so.node +0 -0
  48. package/src/prisma-output/libquery_engine-debian-openssl-3.0.x.so.node +0 -0
  49. package/src/prisma-output/runtime/esm/index-browser.mjs +0 -2370
  50. package/src/prisma-output/runtime/esm/index.mjs +0 -40587
  51. package/src/prisma-output/runtime/esm/proxy.mjs +0 -113
  52. package/src/prisma-output/runtime/index-browser.d.ts +0 -269
  53. package/src/prisma-output/runtime/index-browser.js +0 -2621
  54. package/src/prisma-output/runtime/index.d.ts +0 -1384
  55. package/src/prisma-output/runtime/index.js +0 -59183
  56. package/src/prisma-output/runtime/proxy.d.ts +0 -1384
  57. package/src/prisma-output/runtime/proxy.js +0 -13576
  58. package/src/prisma-output/schema.prisma +0 -47
package/dist/abba.d.ts CHANGED
@@ -1,102 +1,75 @@
1
- import { UserAssignment } from './prisma-output';
2
- import { BucketInput, ExperimentWithBuckets, ExperimentInput, SegmentationData, AssignmentStatistics } from '.';
1
+ import { Saved } from '@naturalcycles/js-lib';
2
+ import { AbbaConfig, Bucket, BucketInput, Experiment, ExperimentWithBuckets, UserAssignment } from './types';
3
+ import { SegmentationData, AssignmentStatistics } from '.';
3
4
  export declare class Abba {
4
- private client;
5
- constructor(dbUrl?: string);
5
+ cfg: AbbaConfig;
6
+ constructor(cfg: AbbaConfig);
7
+ private experimentDao;
8
+ private bucketDao;
9
+ private userAssignmentDao;
6
10
  /**
7
- * Returns all experiments
11
+ * Returns all (active and inactive) experiments.
8
12
  *
9
- * @returns
13
+ * Cold method, not cached.
10
14
  */
11
- getAllExperiments(excludeInactive?: boolean): Promise<ExperimentWithBuckets[]>;
15
+ getAllExperiments(): Promise<ExperimentWithBuckets[]>;
12
16
  /**
13
- * Creates a new experiment
14
- *
15
- * @param experiment
16
- * @param buckets
17
- * @returns
17
+ * Returns only active experiments.
18
+ * Hot method.
19
+ * Cached in-memory for N minutes (currently 10).
18
20
  */
19
- createExperiment(experiment: ExperimentInput, buckets: BucketInput[]): Promise<ExperimentWithBuckets>;
21
+ getActiveExperiments(): Promise<ExperimentWithBuckets[]>;
20
22
  /**
21
- * Update experiment information, will also validate the buckets ratio if experiment.active is true
22
- *
23
- * @param id
24
- * @param experiment
25
- * @param rules
26
- * @param buckets
27
- * @returns
23
+ * Creates a new experiment.
24
+ * Cold method.
28
25
  */
29
- saveExperiment(id: number, experiment: ExperimentInput, buckets: BucketInput[]): Promise<ExperimentWithBuckets>;
26
+ createExperiment(experiment: Experiment, buckets: BucketInput[]): Promise<ExperimentWithBuckets>;
30
27
  /**
31
- * Delete an experiment. Removes all user assignments and buckets
32
- *
33
- * @param id
28
+ * Update experiment information, will also validate the buckets' ratio if experiment.active is true
29
+ * Cold method.
30
+ */
31
+ saveExperiment(experiment: Experiment & {
32
+ id: number;
33
+ }, buckets: Bucket[]): Promise<ExperimentWithBuckets>;
34
+ /**
35
+ * Delete an experiment. Removes all user assignments and buckets.
36
+ * Cold method.
34
37
  */
35
38
  deleteExperiment(id: number): Promise<void>;
36
39
  /**
37
- * Get an assignment for a given user. If existingOnly is false, it will attempt generate a new assignment
40
+ * Get an assignment for a given user. If existingOnly is false, it will attempt to generate a new assignment
41
+ * Cold method.
38
42
  *
39
43
  * @param experimentId
40
44
  * @param userId
41
- * @param createNew
42
- * @param segmentationData
43
- * @returns
45
+ * @param existingOnly Do not generate any new assignments for this experiment
46
+ * @param segmentationData Required if existingOnly is false
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
- * Get all existing user assignments
48
- *
49
- * @param userId G
50
- * @returns
50
+ * Get all existing user assignments.
51
+ * Hot method.
52
+ * Not cached, because Assignments are fast-changing.
51
53
  */
52
- getAllExistingUserAssignments(userId: string): Promise<UserAssignment[]>;
54
+ getAllExistingUserAssignments(userId: string): Promise<Saved<UserAssignment>[]>;
53
55
  /**
54
- * Generate user assignments for all active experiments. Will return any existing and attempt to generate any new assignments.
55
- *
56
- * @param userId
57
- * @param segmentationData
58
- * @returns
56
+ * Generate user assignments for all active experiments.
57
+ * Will return any existing and attempt to generate any new assignments.
58
+ * Hot method.
59
59
  */
60
- generateUserAssignments(userId: string, segmentationData: SegmentationData): Promise<UserAssignment[]>;
60
+ generateUserAssignments(userId: string, segmentationData: SegmentationData): Promise<Saved<UserAssignment>[]>;
61
61
  /**
62
- * Get assignment statistics for an experiment
63
- *
64
- * @param experimentId
65
- * @returns
62
+ * Get assignment statistics for an experiment.
63
+ * Cold method.
66
64
  */
67
65
  getExperimentAssignmentStatistics(experimentId: number): Promise<AssignmentStatistics>;
68
66
  /**
69
- * Generate a new assignment for a given user
70
- *
71
- * @param experimentId
72
- * @param userId
73
- * @param segmentationData
74
- * @returns
67
+ * Generate a new assignment for a given user.
68
+ * Doesn't save it.
75
69
  */
76
- private generateUserAssignment;
70
+ private generateUserAssignmentData;
77
71
  /**
78
72
  * Queries to retrieve an existing user assignment for a given experiment
79
- *
80
- * @param experimentId
81
- * @param userId
82
- * @returns
83
73
  */
84
74
  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
75
  }
package/dist/abba.js CHANGED
@@ -1,245 +1,199 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Abba = void 0;
4
- const prisma_output_1 = require("./prisma-output");
4
+ const tslib_1 = require("tslib");
5
+ const js_lib_1 = require("@naturalcycles/js-lib");
6
+ const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
5
7
  const types_1 = require("./types");
6
8
  const util_1 = require("./util");
7
- // Note: Schema currently contains an output dir which generates all the files to the prisma dir
8
- // it would be tidier not to include it here when possible later on:
9
- // Explanation is here: https://github.com/prisma/prisma/issues/9435#issuecomment-960290681
9
+ const experiment_dao_1 = require("./dao/experiment.dao");
10
+ const userAssignment_dao_1 = require("./dao/userAssignment.dao");
11
+ const bucket_dao_1 = require("./dao/bucket.dao");
12
+ const CACHE_TTL = 600000; // 10 minutes
10
13
  class Abba {
11
- constructor(dbUrl) {
12
- this.client = new prisma_output_1.PrismaClient({
13
- datasources: {
14
- db: {
15
- url: dbUrl,
16
- },
17
- },
18
- });
14
+ constructor(cfg) {
15
+ this.cfg = cfg;
16
+ const { db } = cfg;
17
+ this.experimentDao = (0, experiment_dao_1.experimentDao)(db);
18
+ this.bucketDao = (0, bucket_dao_1.bucketDao)(db);
19
+ this.userAssignmentDao = (0, userAssignment_dao_1.userAssignmentDao)(db);
19
20
  }
20
21
  /**
21
- * Returns all experiments
22
+ * Returns all (active and inactive) experiments.
22
23
  *
23
- * @returns
24
+ * Cold method, not cached.
24
25
  */
25
- async getAllExperiments(excludeInactive = false) {
26
- return await this.client.experiment.findMany({
27
- where: excludeInactive ? { NOT: { status: types_1.AssignmentStatus.Inactive } } : undefined,
28
- include: { buckets: true },
29
- });
26
+ async getAllExperiments() {
27
+ const experiments = await this.experimentDao.query().runQuery();
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
- * Creates a new experiment
33
- *
34
- * @param experiment
35
- * @param buckets
36
- * @returns
38
+ * Returns only active experiments.
39
+ * Hot method.
40
+ * Cached in-memory for N minutes (currently 10).
41
+ */
42
+ async getActiveExperiments() {
43
+ const experiments = await this.experimentDao
44
+ .query()
45
+ .filter('status', '!=', types_1.AssignmentStatus.Inactive)
46
+ .runQuery();
47
+ const buckets = await this.bucketDao
48
+ .query()
49
+ .filter('experimentId', 'in', experiments.map(e => e.id))
50
+ .runQuery();
51
+ return experiments.map(experiment => ({
52
+ ...experiment,
53
+ buckets: buckets.filter(bucket => bucket.experimentId === experiment.id),
54
+ }));
55
+ }
56
+ /**
57
+ * Creates a new experiment.
58
+ * Cold method.
37
59
  */
38
60
  async createExperiment(experiment, buckets) {
39
61
  if (experiment.status === types_1.AssignmentStatus.Active) {
40
62
  (0, util_1.validateTotalBucketRatio)(buckets);
41
63
  }
42
- const created = await this.client.experiment.create({
43
- data: {
44
- ...experiment,
45
- rules: experiment.rules,
46
- buckets: {
47
- createMany: {
48
- data: buckets,
49
- },
50
- },
51
- },
52
- include: {
53
- buckets: true,
54
- },
55
- });
56
- return created;
64
+ await this.experimentDao.save(experiment);
65
+ return {
66
+ ...experiment,
67
+ buckets: await this.bucketDao.saveBatch(buckets.map(b => ({ ...b, experimentId: experiment.id }))),
68
+ };
57
69
  }
58
70
  /**
59
- * Update experiment information, will also validate the buckets ratio if experiment.active is true
60
- *
61
- * @param id
62
- * @param experiment
63
- * @param rules
64
- * @param buckets
65
- * @returns
71
+ * Update experiment information, will also validate the buckets' ratio if experiment.active is true
72
+ * Cold method.
66
73
  */
67
- async saveExperiment(id, experiment, buckets) {
74
+ async saveExperiment(experiment, buckets) {
68
75
  if (experiment.status === types_1.AssignmentStatus.Active) {
69
76
  (0, util_1.validateTotalBucketRatio)(buckets);
70
77
  }
71
- const updatedExperiment = await this.updateExperiment(id, experiment);
72
- const updatedBuckets = await this.saveBuckets(buckets);
78
+ await this.experimentDao.save(experiment);
73
79
  return {
74
- ...updatedExperiment,
75
- buckets: updatedBuckets,
80
+ ...experiment,
81
+ buckets: await this.bucketDao.saveBatch(buckets.map(b => ({ ...b, experimentId: experiment.id }))),
76
82
  };
77
83
  }
78
84
  /**
79
- * Delete an experiment. Removes all user assignments and buckets
80
- *
81
- * @param id
85
+ * Delete an experiment. Removes all user assignments and buckets.
86
+ * Cold method.
82
87
  */
83
88
  async deleteExperiment(id) {
84
- await this.client.experiment.delete({ where: { id } });
89
+ await this.experimentDao.deleteById(id);
85
90
  }
86
91
  /**
87
- * Get an assignment for a given user. If existingOnly is false, it will attempt generate a new assignment
92
+ * Get an assignment for a given user. If existingOnly is false, it will attempt to generate a new assignment
93
+ * Cold method.
88
94
  *
89
95
  * @param experimentId
90
96
  * @param userId
91
- * @param createNew
92
- * @param segmentationData
93
- * @returns
97
+ * @param existingOnly Do not generate any new assignments for this experiment
98
+ * @param segmentationData Required if existingOnly is false
94
99
  */
95
100
  async getUserAssignment(experimentId, userId, existingOnly, segmentationData) {
96
- const experiment = await this.client.experiment.findUnique({
97
- where: { id: experimentId },
98
- include: { buckets: true },
99
- });
100
- if (!experiment)
101
- throw new Error('Experiment not found');
102
101
  const existing = await this.getExistingUserAssignment(experimentId, userId);
103
102
  if (existing)
104
103
  return existing;
105
- if (experiment.status !== types_1.AssignmentStatus.Active || existingOnly)
104
+ if (existingOnly)
105
+ return null;
106
+ const experiment = await this.experimentDao.requireById(experimentId);
107
+ if (experiment.status !== types_1.AssignmentStatus.Active)
108
+ return null;
109
+ (0, js_lib_1._assert)(segmentationData, 'Segmentation data required when creating a new assignment');
110
+ const buckets = await this.bucketDao.getBy('experimentId', experimentId);
111
+ const assignment = this.generateUserAssignmentData({ ...experiment, buckets }, userId, segmentationData);
112
+ if (!assignment)
106
113
  return null;
107
- if (!segmentationData)
108
- throw new Error('Segmentation data required when creating a new assignment');
109
- return await this.generateUserAssignment(experiment, userId, segmentationData);
114
+ return await this.userAssignmentDao.save(assignment);
110
115
  }
111
116
  /**
112
- * Get all existing user assignments
113
- *
114
- * @param userId G
115
- * @returns
117
+ * Get all existing user assignments.
118
+ * Hot method.
119
+ * Not cached, because Assignments are fast-changing.
116
120
  */
117
121
  async getAllExistingUserAssignments(userId) {
118
- return await this.client.userAssignment.findMany({ where: { userId } });
122
+ return await this.userAssignmentDao.getBy('userId', userId);
119
123
  }
120
124
  /**
121
- * Generate user assignments for all active experiments. Will return any existing and attempt to generate any new assignments.
122
- *
123
- * @param userId
124
- * @param segmentationData
125
- * @returns
125
+ * Generate user assignments for all active experiments.
126
+ * Will return any existing and attempt to generate any new assignments.
127
+ * Hot method.
126
128
  */
127
129
  async generateUserAssignments(userId, segmentationData) {
128
- const experiments = await this.getAllExperiments(true);
130
+ const experiments = await this.getActiveExperiments(); // cached
129
131
  const existingAssignments = await this.getAllExistingUserAssignments(userId);
130
- const assignments = [];
132
+ const allAssignments = [];
131
133
  const generatedAssignments = [];
132
134
  for (const experiment of experiments) {
133
135
  const existing = existingAssignments.find(ua => ua.experimentId === experiment.id);
134
136
  if (existing) {
135
- assignments.push(existing);
136
- continue;
137
+ allAssignments.push(existing);
137
138
  }
138
139
  else {
139
- generatedAssignments.push(this.generateUserAssignment(experiment, userId, segmentationData));
140
+ const assignment = this.generateUserAssignmentData(experiment, userId, segmentationData);
141
+ if (assignment) {
142
+ generatedAssignments.push(assignment);
143
+ }
140
144
  }
141
145
  }
142
- const generated = await Promise.all(generatedAssignments);
143
- const filtered = generated.filter((ua) => ua !== null);
144
- return [...assignments, ...filtered];
146
+ await this.userAssignmentDao.saveBatch(generatedAssignments);
147
+ return [...allAssignments, ...generatedAssignments];
145
148
  }
146
149
  /**
147
- * Get assignment statistics for an experiment
148
- *
149
- * @param experimentId
150
- * @returns
150
+ * Get assignment statistics for an experiment.
151
+ * Cold method.
151
152
  */
152
153
  async getExperimentAssignmentStatistics(experimentId) {
153
154
  const statistics = {
154
- sampled: await this.client.userAssignment.count({ where: { experimentId } }),
155
+ sampled: await this.userAssignmentDao
156
+ .query()
157
+ .filterEq('experimentId', experimentId)
158
+ .runQueryCount(),
155
159
  buckets: {},
156
160
  };
157
- const buckets = await this.client.bucket.findMany({ where: { experimentId } });
158
- const assignmentCounts = await this.client.userAssignment.groupBy({
159
- where: { experimentId },
160
- by: ['bucketId'],
161
- _count: {
162
- _all: true,
163
- },
164
- });
165
- buckets.forEach(({ id }) => {
166
- statistics.buckets[`${id}`] = assignmentCounts.find(i => i.bucketId === id)?._count?._all || 0;
161
+ const buckets = await this.bucketDao.getBy('experimentId', experimentId);
162
+ await (0, js_lib_1.pMap)(buckets, async (bucket) => {
163
+ statistics[bucket.id] = await this.userAssignmentDao
164
+ .query()
165
+ .filterEq('bucketId', bucket.id)
166
+ .runQueryCount();
167
167
  });
168
168
  return statistics;
169
169
  }
170
170
  /**
171
- * Generate a new assignment for a given user
172
- *
173
- * @param experimentId
174
- * @param userId
175
- * @param segmentationData
176
- * @returns
171
+ * Generate a new assignment for a given user.
172
+ * Doesn't save it.
177
173
  */
178
- async generateUserAssignment(experiment, userId, segmentationData) {
174
+ generateUserAssignmentData(experiment, userId, segmentationData) {
179
175
  const segmentationMatch = (0, util_1.validateSegmentationRules)(experiment.rules, segmentationData);
180
176
  if (!segmentationMatch)
181
177
  return null;
182
- const bucketId = (0, util_1.determineAssignment)(experiment.sampling, experiment.buckets);
183
- return await this.client.userAssignment.create({
184
- data: { userId, experimentId: experiment.id, bucketId },
185
- });
178
+ return {
179
+ userId,
180
+ experimentId: experiment.id,
181
+ bucketId: (0, util_1.determineAssignment)(experiment.sampling, experiment.buckets),
182
+ };
186
183
  }
187
184
  /**
188
185
  * Queries to retrieve an existing user assignment for a given experiment
189
- *
190
- * @param experimentId
191
- * @param userId
192
- * @returns
193
186
  */
194
187
  async getExistingUserAssignment(experimentId, userId) {
195
- return await this.client.userAssignment.findFirst({ where: { userId, experimentId } });
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);
188
+ const [assignment] = await this.userAssignmentDao
189
+ .query()
190
+ .filterEq('userId', userId)
191
+ .filterEq('experimentId', experimentId)
192
+ .runQuery();
193
+ return assignment || null;
243
194
  }
244
195
  }
196
+ tslib_1.__decorate([
197
+ (0, js_lib_1._AsyncMemo)({ cacheFactory: () => new nodejs_lib_1.LRUMemoCache({ ttl: CACHE_TTL, max: 1 }) })
198
+ ], Abba.prototype, "getActiveExperiments", null);
245
199
  exports.Abba = Abba;
@@ -0,0 +1,5 @@
1
+ import { CommonDao, CommonDB } from '@naturalcycles/db-lib';
2
+ import { Bucket } from '../types';
3
+ export declare class BucketDao extends CommonDao<Bucket> {
4
+ }
5
+ export declare const bucketDao: (db: CommonDB) => BucketDao;
@@ -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,5 @@
1
+ import { CommonDao, CommonDB } from '@naturalcycles/db-lib';
2
+ import { UserAssignment } from '../types';
3
+ export declare class UserAssignmentDao extends CommonDao<UserAssignment> {
4
+ }
5
+ export declare const userAssignmentDao: (db: CommonDB) => UserAssignmentDao;
@@ -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
@@ -1,3 +1,2 @@
1
- export type { Bucket, Experiment, UserAssignment } from './prisma-output';
2
1
  export * from './types';
3
2
  export { Abba } from './abba';
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
- (0, tslib_1.__exportStar)(require("./types"), exports);
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;