@eventvisor/sdk 0.0.2
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/LICENSE +21 -0
- package/README.md +9 -0
- package/dist/attributesManager.d.ts +36 -0
- package/dist/bucketer.d.ts +30 -0
- package/dist/compareVersions.d.ts +4 -0
- package/dist/conditions.d.ts +20 -0
- package/dist/datafileReader.d.ts +29 -0
- package/dist/effectsManager.d.ts +33 -0
- package/dist/emitter.d.ts +11 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +2 -0
- package/dist/index.mjs.gz +0 -0
- package/dist/index.mjs.map +1 -0
- package/dist/instance.d.ts +67 -0
- package/dist/logger.d.ts +26 -0
- package/dist/modulesManager.d.ts +67 -0
- package/dist/murmurhash.d.ts +1 -0
- package/dist/persister.d.ts +40 -0
- package/dist/sourceResolver.d.ts +31 -0
- package/dist/transformer.d.ts +21 -0
- package/dist/validator.d.ts +28 -0
- package/jest.config.js +6 -0
- package/lib/attributesManager.d.ts +36 -0
- package/lib/bucketer.d.ts +30 -0
- package/lib/compareVersions.d.ts +4 -0
- package/lib/conditions.d.ts +20 -0
- package/lib/datafileReader.d.ts +29 -0
- package/lib/effectsManager.d.ts +33 -0
- package/lib/emitter.d.ts +11 -0
- package/lib/index.d.ts +12 -0
- package/lib/instance.d.ts +67 -0
- package/lib/logger.d.ts +26 -0
- package/lib/modulesManager.d.ts +67 -0
- package/lib/murmurhash.d.ts +1 -0
- package/lib/persister.d.ts +40 -0
- package/lib/sourceResolver.d.ts +31 -0
- package/lib/transformer.d.ts +21 -0
- package/lib/validator.d.ts +28 -0
- package/package.json +45 -0
- package/src/attributesManager.ts +181 -0
- package/src/bucketer.spec.ts +156 -0
- package/src/bucketer.ts +152 -0
- package/src/compareVersions.ts +93 -0
- package/src/conditions.ts +224 -0
- package/src/datafileReader.ts +133 -0
- package/src/effectsManager.ts +214 -0
- package/src/emitter.ts +64 -0
- package/src/index.spec.ts +5 -0
- package/src/index.ts +14 -0
- package/src/instance.spec.ts +184 -0
- package/src/instance.ts +608 -0
- package/src/logger.ts +90 -0
- package/src/modulesManager.ts +276 -0
- package/src/murmurhash.ts +71 -0
- package/src/persister.ts +162 -0
- package/src/sourceResolver.spec.ts +253 -0
- package/src/sourceResolver.ts +213 -0
- package/src/transformer.ts +316 -0
- package/src/transformer_static.spec.ts +377 -0
- package/src/transformer_types.spec.ts +820 -0
- package/src/validator.spec.ts +579 -0
- package/src/validator.ts +366 -0
- package/tsconfig.cjs.json +8 -0
- package/tsconfig.esm.json +8 -0
- package/webpack.config.js +80 -0
package/src/instance.ts
ADDED
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AttributeName,
|
|
3
|
+
DatafileContent,
|
|
4
|
+
EventName,
|
|
5
|
+
EffectName,
|
|
6
|
+
Value,
|
|
7
|
+
Action,
|
|
8
|
+
} from "@eventvisor/types";
|
|
9
|
+
|
|
10
|
+
import { DatafileReader, emptyDatafile } from "./datafileReader";
|
|
11
|
+
import { createLogger, Logger, LogLevel } from "./logger";
|
|
12
|
+
import { Emitter, EmitType, EventCallback } from "./emitter";
|
|
13
|
+
import { AttributesManager } from "./attributesManager";
|
|
14
|
+
import { Module, ModuleName, ModulesManager } from "./modulesManager";
|
|
15
|
+
import { SourceResolver } from "./sourceResolver";
|
|
16
|
+
import { ConditionsChecker } from "./conditions";
|
|
17
|
+
import { Bucketer } from "./bucketer";
|
|
18
|
+
import { Transformer } from "./transformer";
|
|
19
|
+
import { Validator } from "./validator";
|
|
20
|
+
import { EffectsManager } from "./effectsManager";
|
|
21
|
+
|
|
22
|
+
export interface InstanceOptions {
|
|
23
|
+
datafile?: DatafileContent;
|
|
24
|
+
logLevel?: LogLevel;
|
|
25
|
+
logger?: Logger;
|
|
26
|
+
modules?: Module[];
|
|
27
|
+
|
|
28
|
+
// @TODO
|
|
29
|
+
// initialAttributes?: Record<AttributeName, Value>;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export class Eventvisor {
|
|
33
|
+
private datafileReader: DatafileReader;
|
|
34
|
+
private logger: Logger;
|
|
35
|
+
private emitter: Emitter;
|
|
36
|
+
private attributesManager: AttributesManager;
|
|
37
|
+
private modulesManager: ModulesManager;
|
|
38
|
+
private effectsManager: EffectsManager;
|
|
39
|
+
private sourceResolver: SourceResolver;
|
|
40
|
+
private conditionsChecker: ConditionsChecker;
|
|
41
|
+
private transformer: Transformer;
|
|
42
|
+
private bucketer: Bucketer;
|
|
43
|
+
private validator: Validator;
|
|
44
|
+
|
|
45
|
+
private ready: boolean = false;
|
|
46
|
+
private queue: Action[] = [];
|
|
47
|
+
private queueProcessing: boolean = false;
|
|
48
|
+
|
|
49
|
+
constructor(options: InstanceOptions = {}) {
|
|
50
|
+
/**
|
|
51
|
+
* Core instances without interdependencies
|
|
52
|
+
*
|
|
53
|
+
* @TODO: sort out this dependency mess!!
|
|
54
|
+
*/
|
|
55
|
+
this.logger =
|
|
56
|
+
options.logger ||
|
|
57
|
+
createLogger({
|
|
58
|
+
level: options.logLevel || Logger.defaultLevel,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
this.datafileReader = new DatafileReader({
|
|
62
|
+
datafile: options.datafile || emptyDatafile,
|
|
63
|
+
logger: this.logger,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
this.emitter = new Emitter();
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Instances with interdependencies
|
|
70
|
+
*/
|
|
71
|
+
this.modulesManager = new ModulesManager({
|
|
72
|
+
logger: this.logger,
|
|
73
|
+
getDatafileReader: () => this.datafileReader,
|
|
74
|
+
getSourceResolver: () => this.sourceResolver,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
this.validator = new Validator({
|
|
78
|
+
logger: this.logger,
|
|
79
|
+
getSourceResolver: () => this.sourceResolver,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
this.attributesManager = new AttributesManager({
|
|
83
|
+
logger: this.logger,
|
|
84
|
+
emitter: this.emitter,
|
|
85
|
+
validator: this.validator,
|
|
86
|
+
getDatafileReader: () => this.datafileReader,
|
|
87
|
+
getTransformer: () => this.transformer,
|
|
88
|
+
getConditionsChecker: () => this.conditionsChecker,
|
|
89
|
+
modulesManager: this.modulesManager,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
this.effectsManager = new EffectsManager({
|
|
93
|
+
logger: this.logger,
|
|
94
|
+
getDatafileReader: () => this.datafileReader,
|
|
95
|
+
getTransformer: () => this.transformer,
|
|
96
|
+
getConditionsChecker: () => this.conditionsChecker,
|
|
97
|
+
modulesManager: this.modulesManager,
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
this.sourceResolver = new SourceResolver({
|
|
101
|
+
logger: this.logger,
|
|
102
|
+
modulesManager: this.modulesManager,
|
|
103
|
+
attributesManager: this.attributesManager,
|
|
104
|
+
effectsManager: this.effectsManager,
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
this.conditionsChecker = new ConditionsChecker({
|
|
108
|
+
logger: this.logger,
|
|
109
|
+
getRegex: (regexString, regexFlags) => this.datafileReader.getRegex(regexString, regexFlags),
|
|
110
|
+
sourceResolver: this.sourceResolver,
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
this.transformer = new Transformer({
|
|
114
|
+
logger: this.logger,
|
|
115
|
+
conditionsChecker: this.conditionsChecker,
|
|
116
|
+
sourceResolver: this.sourceResolver,
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
this.bucketer = new Bucketer({
|
|
120
|
+
logger: this.logger,
|
|
121
|
+
sourceResolver: this.sourceResolver,
|
|
122
|
+
conditionsChecker: this.conditionsChecker,
|
|
123
|
+
transformer: this.transformer,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Ready
|
|
128
|
+
*/
|
|
129
|
+
if (options.modules) {
|
|
130
|
+
for (const module of options.modules) {
|
|
131
|
+
this.modulesManager.registerModule(module);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
Promise.all([this.effectsManager.initialize(), this.attributesManager.initialize()])
|
|
136
|
+
.then(() => {
|
|
137
|
+
this.ready = true;
|
|
138
|
+
this.emitter.trigger("ready");
|
|
139
|
+
this.logger.debug("Eventvisor SDK is ready");
|
|
140
|
+
})
|
|
141
|
+
.catch((error) => {
|
|
142
|
+
this.logger.error("initialization failed", error);
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
this.logger.info("Eventvisor SDK initialized");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
isReady() {
|
|
149
|
+
return this.ready;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async onReady(): Promise<void> {
|
|
153
|
+
if (this.ready) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return new Promise((resolve) => {
|
|
158
|
+
const unsubscribe = this.emitter.on("ready", () => {
|
|
159
|
+
unsubscribe();
|
|
160
|
+
resolve();
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
getRevision() {
|
|
166
|
+
return this.datafileReader.getRevision();
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
setLogLevel(level: LogLevel) {
|
|
170
|
+
return this.logger.setLevel(level);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
setDatafile(datafile: DatafileContent) {
|
|
174
|
+
try {
|
|
175
|
+
const newDatafileReader = new DatafileReader({
|
|
176
|
+
datafile,
|
|
177
|
+
logger: this.logger,
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
this.datafileReader = newDatafileReader;
|
|
181
|
+
|
|
182
|
+
this.effectsManager.refresh();
|
|
183
|
+
|
|
184
|
+
this.emitter.trigger("datafile_set");
|
|
185
|
+
} catch (error) {
|
|
186
|
+
this.logger.error("Error setting datafile", {
|
|
187
|
+
error,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
on(emitType: EmitType, callback: EventCallback) {
|
|
193
|
+
return this.emitter.on(emitType, callback);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Queue
|
|
198
|
+
*/
|
|
199
|
+
private addToQueue(action: Action) {
|
|
200
|
+
this.queue.push(action);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// @TODO: make it better
|
|
204
|
+
private async processQueue() {
|
|
205
|
+
if (this.queue.length === 0) {
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (this.queueProcessing) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
this.queueProcessing = true;
|
|
214
|
+
|
|
215
|
+
const action = this.queue.shift();
|
|
216
|
+
|
|
217
|
+
if (!action) {
|
|
218
|
+
this.queueProcessing = false;
|
|
219
|
+
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
if (action.type === "track") {
|
|
225
|
+
await this.trackAsync(action.name, action.value);
|
|
226
|
+
} else if (action.type === "setAttribute") {
|
|
227
|
+
await this.setAttributeAsync(action.name, action.value);
|
|
228
|
+
} else if (action.type === "removeAttribute") {
|
|
229
|
+
await this.removeAttributeAsync(action.name);
|
|
230
|
+
} else if (action.type === "removeAttribute") {
|
|
231
|
+
await this.removeAttributeAsync(action.name);
|
|
232
|
+
}
|
|
233
|
+
} catch (error) {
|
|
234
|
+
this.logger.error(`Error processing queue`, {
|
|
235
|
+
error,
|
|
236
|
+
action,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
this.queueProcessing = false;
|
|
241
|
+
|
|
242
|
+
await this.processQueue();
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Attribute
|
|
247
|
+
*/
|
|
248
|
+
async setAttributeAsync(attributeName: AttributeName, value: Value) {
|
|
249
|
+
const result = await this.attributesManager.setAttribute(attributeName, value);
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Effects
|
|
253
|
+
*/
|
|
254
|
+
await this.effectsManager.dispatch({
|
|
255
|
+
eventType: "attribute_set",
|
|
256
|
+
name: attributeName,
|
|
257
|
+
value: result,
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
return result;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
setAttribute(attributeName: AttributeName, value: Value) {
|
|
264
|
+
this.addToQueue({
|
|
265
|
+
type: "setAttribute",
|
|
266
|
+
name: attributeName,
|
|
267
|
+
value,
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
this.processQueue();
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
getAttributeValue(attributeName: AttributeName) {
|
|
274
|
+
return this.attributesManager.getAttributeValue(attributeName);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
getAttributes() {
|
|
278
|
+
return this.attributesManager.getAttributesMap();
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
isAttributeSet(attributeName: AttributeName) {
|
|
282
|
+
return this.attributesManager.isAttributeSet(attributeName);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
removeAttributeAsync(attributeName: AttributeName) {
|
|
286
|
+
return this.attributesManager.removeAttribute(attributeName);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
removeAttribute(attributeName: AttributeName) {
|
|
290
|
+
this.addToQueue({
|
|
291
|
+
type: "removeAttribute",
|
|
292
|
+
name: attributeName,
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
this.processQueue();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Modules
|
|
300
|
+
*/
|
|
301
|
+
registerModule(module: Module) {
|
|
302
|
+
return this.modulesManager.registerModule(module);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
removeModule(moduleName: ModuleName) {
|
|
306
|
+
return this.modulesManager.removeModule(moduleName);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Event
|
|
311
|
+
*/
|
|
312
|
+
async trackAsync(eventName: EventName, value: Value): Promise<Value | null> {
|
|
313
|
+
/**
|
|
314
|
+
* Find
|
|
315
|
+
*/
|
|
316
|
+
const eventSchema = this.datafileReader.getEvent(eventName);
|
|
317
|
+
|
|
318
|
+
if (!eventSchema) {
|
|
319
|
+
this.logger.error(`Event schema not found in datafile`, { eventName });
|
|
320
|
+
|
|
321
|
+
return null; // @TODO: allow to continue based on SDK instance options later
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const eventLevel = eventSchema.level || "info";
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* Deprecated
|
|
328
|
+
*/
|
|
329
|
+
if (eventSchema.deprecated) {
|
|
330
|
+
this.logger.warn(`Event is deprecated`, { eventName });
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Validate
|
|
335
|
+
*/
|
|
336
|
+
const validationResult = await this.validator.validate(eventSchema, value);
|
|
337
|
+
|
|
338
|
+
if (!validationResult.valid) {
|
|
339
|
+
this.logger.warn(`Event validation failed`, {
|
|
340
|
+
eventName,
|
|
341
|
+
errors: validationResult.errors,
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
return null; // @TODO: allow to continue based on schema later
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const validatedValue = validationResult.value;
|
|
348
|
+
|
|
349
|
+
/**
|
|
350
|
+
* Conditions
|
|
351
|
+
*/
|
|
352
|
+
if (eventSchema.conditions) {
|
|
353
|
+
const isMatched = await this.conditionsChecker.allAreMatched(eventSchema.conditions, {
|
|
354
|
+
// @TODO: rename to eventPayload to be explicit?
|
|
355
|
+
eventName,
|
|
356
|
+
eventLevel,
|
|
357
|
+
payload: validatedValue,
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
if (!isMatched) {
|
|
361
|
+
this.logger.debug(`Event conditions not matched`, {
|
|
362
|
+
eventName,
|
|
363
|
+
conditions: eventSchema.conditions,
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
return null;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/**
|
|
371
|
+
* Sample
|
|
372
|
+
*/
|
|
373
|
+
if (eventSchema.sample) {
|
|
374
|
+
const sampleResult = await this.bucketer.isSampled(eventSchema.sample, {
|
|
375
|
+
eventName,
|
|
376
|
+
eventLevel,
|
|
377
|
+
payload: validatedValue,
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
if (!sampleResult.isSampled) {
|
|
381
|
+
this.logger.debug(`Event sample not matched`, {
|
|
382
|
+
eventName,
|
|
383
|
+
matchedSample: sampleResult.matchedSample,
|
|
384
|
+
bucketedNumber: sampleResult.bucketedNumber,
|
|
385
|
+
bucketKey: sampleResult.bucketKey,
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
return null;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Transform
|
|
394
|
+
*/
|
|
395
|
+
let transformedValue = validatedValue;
|
|
396
|
+
|
|
397
|
+
if (eventSchema.transforms) {
|
|
398
|
+
transformedValue = await this.transformer.applyAll(validatedValue, eventSchema.transforms, {
|
|
399
|
+
eventName,
|
|
400
|
+
eventLevel,
|
|
401
|
+
payload: validatedValue,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Effects
|
|
407
|
+
*/
|
|
408
|
+
await this.effectsManager.dispatch({
|
|
409
|
+
eventType: "event_tracked",
|
|
410
|
+
name: eventName,
|
|
411
|
+
value: transformedValue,
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Destinations
|
|
416
|
+
*/
|
|
417
|
+
const destinationNames = this.datafileReader.getDestinationNames();
|
|
418
|
+
|
|
419
|
+
for (const destinationName of destinationNames) {
|
|
420
|
+
const destination = this.datafileReader.getDestination(destinationName);
|
|
421
|
+
|
|
422
|
+
if (!destination) {
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const transportExists = this.modulesManager.transportExists(destination.transport);
|
|
427
|
+
|
|
428
|
+
if (!transportExists) {
|
|
429
|
+
this.logger.error(`Destination has no transport`, {
|
|
430
|
+
eventName,
|
|
431
|
+
destinationName,
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
continue;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
let transportBody = transformedValue;
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Event.destinations
|
|
441
|
+
*/
|
|
442
|
+
if (
|
|
443
|
+
eventSchema.destinations &&
|
|
444
|
+
typeof eventSchema.destinations[destinationName] !== "undefined"
|
|
445
|
+
) {
|
|
446
|
+
const destinationOverride = eventSchema.destinations[destinationName];
|
|
447
|
+
|
|
448
|
+
if (destinationOverride === false) {
|
|
449
|
+
this.logger.debug(`Event has destination disabled`, {
|
|
450
|
+
eventName,
|
|
451
|
+
destinationName,
|
|
452
|
+
});
|
|
453
|
+
|
|
454
|
+
continue;
|
|
455
|
+
} else if (typeof destinationOverride === "object") {
|
|
456
|
+
// conditions
|
|
457
|
+
if (destinationOverride.conditions) {
|
|
458
|
+
const isMatched = await this.conditionsChecker.allAreMatched(
|
|
459
|
+
destinationOverride.conditions,
|
|
460
|
+
{
|
|
461
|
+
eventName,
|
|
462
|
+
eventLevel,
|
|
463
|
+
payload: transportBody,
|
|
464
|
+
},
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
if (!isMatched) {
|
|
468
|
+
this.logger.debug(`Destination conditions not matched for event`, {
|
|
469
|
+
eventName,
|
|
470
|
+
destinationName,
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
continue;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// sample
|
|
477
|
+
if (destinationOverride.sample) {
|
|
478
|
+
const sampleResult = await this.bucketer.isSampled(destinationOverride.sample, {
|
|
479
|
+
eventName,
|
|
480
|
+
eventLevel,
|
|
481
|
+
payload: transportBody,
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
if (!sampleResult.isSampled) {
|
|
485
|
+
this.logger.debug(`Destination sample not matched for event`, {
|
|
486
|
+
eventName,
|
|
487
|
+
destinationName,
|
|
488
|
+
matchedSample: sampleResult.matchedSample,
|
|
489
|
+
bucketedNumber: sampleResult.bucketedNumber,
|
|
490
|
+
bucketKey: sampleResult.bucketKey,
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// transform
|
|
498
|
+
if (destinationOverride.transforms) {
|
|
499
|
+
// @TODO: make sure this transformed value is only affecting the specific desired destination and not others
|
|
500
|
+
transportBody = await this.transformer.applyAll(
|
|
501
|
+
transformedValue,
|
|
502
|
+
destinationOverride.transforms,
|
|
503
|
+
{
|
|
504
|
+
eventName,
|
|
505
|
+
eventLevel,
|
|
506
|
+
payload: transportBody,
|
|
507
|
+
},
|
|
508
|
+
);
|
|
509
|
+
}
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Destination itself
|
|
516
|
+
*/
|
|
517
|
+
|
|
518
|
+
// conditions
|
|
519
|
+
if (destination.conditions) {
|
|
520
|
+
const isMatched = await this.conditionsChecker.allAreMatched(destination.conditions, {
|
|
521
|
+
eventName,
|
|
522
|
+
eventLevel,
|
|
523
|
+
payload: transformedValue,
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
if (!isMatched) {
|
|
527
|
+
this.logger.debug(`Destination conditions not matched`, {
|
|
528
|
+
eventName,
|
|
529
|
+
destinationName,
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
continue;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
// sample
|
|
537
|
+
if (destination.sample) {
|
|
538
|
+
const sampleResult = await this.bucketer.isSampled(destination.sample, {
|
|
539
|
+
eventName,
|
|
540
|
+
eventLevel,
|
|
541
|
+
payload: transportBody,
|
|
542
|
+
});
|
|
543
|
+
|
|
544
|
+
if (!sampleResult.isSampled) {
|
|
545
|
+
this.logger.debug(`Destination sample not matched`, {
|
|
546
|
+
eventName,
|
|
547
|
+
destinationName,
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
continue;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
// transform
|
|
555
|
+
if (destination.transforms) {
|
|
556
|
+
transportBody = await this.transformer.applyAll(transportBody, destination.transforms, {
|
|
557
|
+
eventName,
|
|
558
|
+
eventLevel,
|
|
559
|
+
payload: transportBody,
|
|
560
|
+
destinationName,
|
|
561
|
+
attributes: this.attributesManager.getAttributesMap(), // @TODO: check if needed
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
// hand over to module for transporting
|
|
566
|
+
// @TODO: decide about "await" or not
|
|
567
|
+
// @TODO: batch
|
|
568
|
+
// @TODO: retry
|
|
569
|
+
await this.modulesManager.transport(
|
|
570
|
+
destination.transport,
|
|
571
|
+
destinationName,
|
|
572
|
+
eventName,
|
|
573
|
+
transportBody,
|
|
574
|
+
eventLevel,
|
|
575
|
+
);
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
return transformedValue;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
track(eventName: EventName, value: Value) {
|
|
582
|
+
this.addToQueue({
|
|
583
|
+
type: "track",
|
|
584
|
+
name: eventName,
|
|
585
|
+
value,
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
this.processQueue();
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/**
|
|
592
|
+
* Effect's state
|
|
593
|
+
*/
|
|
594
|
+
getStateValue(name: EffectName) {
|
|
595
|
+
return this.effectsManager.getStateValue(name);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* @TODO: implement
|
|
600
|
+
*/
|
|
601
|
+
spawn() {
|
|
602
|
+
// create child instance here
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
export function createInstance(options: InstanceOptions = {}): Eventvisor {
|
|
607
|
+
return new Eventvisor(options);
|
|
608
|
+
}
|
package/src/logger.ts
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export type LogLevel = "fatal" | "error" | "warn" | "info" | "debug";
|
|
2
|
+
|
|
3
|
+
export type LogMessage = string;
|
|
4
|
+
|
|
5
|
+
export interface LogDetails {
|
|
6
|
+
[key: string]: any;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export type LogHandler = (level: LogLevel, message: LogMessage, details?: LogDetails) => void;
|
|
10
|
+
|
|
11
|
+
export interface CreateLoggerOptions {
|
|
12
|
+
level?: LogLevel;
|
|
13
|
+
handler?: LogHandler;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const loggerPrefix = "[Eventvisor]";
|
|
17
|
+
|
|
18
|
+
export const defaultLogHandler: LogHandler = function defaultLogHandler(
|
|
19
|
+
level,
|
|
20
|
+
message,
|
|
21
|
+
details = {},
|
|
22
|
+
) {
|
|
23
|
+
let method = "log";
|
|
24
|
+
|
|
25
|
+
if (level === "info") {
|
|
26
|
+
method = "info";
|
|
27
|
+
} else if (level === "warn") {
|
|
28
|
+
method = "warn";
|
|
29
|
+
} else if (level === "error") {
|
|
30
|
+
method = "error";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
console[method](loggerPrefix, message, details);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export class Logger {
|
|
37
|
+
static allLevels: LogLevel[] = [
|
|
38
|
+
"fatal",
|
|
39
|
+
"error",
|
|
40
|
+
"warn",
|
|
41
|
+
"info",
|
|
42
|
+
|
|
43
|
+
// not enabled by default
|
|
44
|
+
"debug",
|
|
45
|
+
];
|
|
46
|
+
|
|
47
|
+
static defaultLevel: LogLevel = "info";
|
|
48
|
+
|
|
49
|
+
private level: LogLevel;
|
|
50
|
+
private handle: LogHandler;
|
|
51
|
+
|
|
52
|
+
constructor(options: CreateLoggerOptions) {
|
|
53
|
+
this.level = options.level || Logger.defaultLevel;
|
|
54
|
+
this.handle = options.handler || defaultLogHandler;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
setLevel(level: LogLevel) {
|
|
58
|
+
this.level = level;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
log(level: LogLevel, message: LogMessage, details?: LogDetails) {
|
|
62
|
+
const shouldHandle = Logger.allLevels.indexOf(this.level) >= Logger.allLevels.indexOf(level);
|
|
63
|
+
|
|
64
|
+
if (!shouldHandle) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
this.handle(level, message, details);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
debug(message: LogMessage, details?: LogDetails) {
|
|
72
|
+
this.log("debug", message, details);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
info(message: LogMessage, details?: LogDetails) {
|
|
76
|
+
this.log("info", message, details);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
warn(message: LogMessage, details?: LogDetails) {
|
|
80
|
+
this.log("warn", message, details);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
error(message: LogMessage, details?: LogDetails) {
|
|
84
|
+
this.log("error", message, details);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function createLogger(options: CreateLoggerOptions = {}): Logger {
|
|
89
|
+
return new Logger(options);
|
|
90
|
+
}
|