@nu-art/analytics-backend 0.400.7
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/_modules/ModuleBE_Analytics.d.ts +19 -0
- package/_modules/ModuleBE_Analytics.js +40 -0
- package/index.d.ts +1 -0
- package/index.js +1 -0
- package/package.json +38 -0
- package/plugins/AnalyticsPlugin_Base.d.ts +41 -0
- package/plugins/AnalyticsPlugin_Base.js +61 -0
- package/plugins/AnalyticsPlugin_Logger.d.ts +9 -0
- package/plugins/AnalyticsPlugin_Logger.js +14 -0
- package/plugins/AnalyticsPlugin_MixedPanels.d.ts +30 -0
- package/plugins/AnalyticsPlugin_MixedPanels.js +98 -0
- package/plugins/index.d.ts +4 -0
- package/plugins/index.js +4 -0
- package/plugins/registry.d.ts +6 -0
- package/plugins/registry.js +2 -0
- package/plugins/types.d.ts +6 -0
- package/plugins/types.js +1 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Module } from '@nu-art/ts-common';
|
|
2
|
+
import { AnalyticsPlugin_Base } from '../plugins/AnalyticsPlugin_Base.js';
|
|
3
|
+
import { AnalyticsPluginRegistry } from '../plugins/index.js';
|
|
4
|
+
type Config = {
|
|
5
|
+
plugins: {
|
|
6
|
+
[K in keyof AnalyticsPluginRegistry]: AnalyticsPluginRegistry[K] extends AnalyticsPlugin_Base<any, infer C> ? C : never;
|
|
7
|
+
};
|
|
8
|
+
};
|
|
9
|
+
declare class ModuleBE_Analytics_Class extends Module<Config> {
|
|
10
|
+
private readonly plugins;
|
|
11
|
+
protected init(): void;
|
|
12
|
+
addPlugin(plugin: AnalyticsPlugin_Base): void;
|
|
13
|
+
removePlugin(plugin: AnalyticsPlugin_Base): void;
|
|
14
|
+
private initPlugins;
|
|
15
|
+
private api_sendEvent;
|
|
16
|
+
private api_updateUser;
|
|
17
|
+
}
|
|
18
|
+
export declare const ModuleBE_Analytics: ModuleBE_Analytics_Class;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Module } from '@nu-art/ts-common';
|
|
2
|
+
import { addRoutes, createBodyServerApi } from '@nu-art/thunderstorm-backend';
|
|
3
|
+
import { ApiDef_Analytics } from '@nu-art/analytics-shared';
|
|
4
|
+
class ModuleBE_Analytics_Class extends Module {
|
|
5
|
+
plugins = new Map();
|
|
6
|
+
init() {
|
|
7
|
+
super.init();
|
|
8
|
+
this.initPlugins();
|
|
9
|
+
addRoutes([
|
|
10
|
+
createBodyServerApi(ApiDef_Analytics()._v1.sendEvent, this.api_sendEvent),
|
|
11
|
+
createBodyServerApi(ApiDef_Analytics()._v1.updateUser, this.api_updateUser),
|
|
12
|
+
]);
|
|
13
|
+
}
|
|
14
|
+
//######################### Plugin Management #########################
|
|
15
|
+
addPlugin(plugin) {
|
|
16
|
+
this.plugins.set(plugin.key, plugin);
|
|
17
|
+
}
|
|
18
|
+
removePlugin(plugin) {
|
|
19
|
+
this.plugins.delete(plugin.key);
|
|
20
|
+
}
|
|
21
|
+
initPlugins() {
|
|
22
|
+
this.plugins.forEach(plugin => {
|
|
23
|
+
const key = plugin.key;
|
|
24
|
+
const pluginConfig = this.config.plugins[key];
|
|
25
|
+
plugin.init(pluginConfig);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
//######################### API Callbacks #########################
|
|
29
|
+
api_sendEvent = async (request) => {
|
|
30
|
+
return this.plugins.forEach(plugin => {
|
|
31
|
+
plugin.registerEvent(request.event);
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
api_updateUser = async (request) => {
|
|
35
|
+
return this.plugins.forEach(plugin => {
|
|
36
|
+
plugin.updateUser?.(request);
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
export const ModuleBE_Analytics = new ModuleBE_Analytics_Class();
|
package/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './_modules/ModuleBE_Analytics.js';
|
package/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './_modules/ModuleBE_Analytics.js';
|
package/package.json
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@nu-art/analytics-backend",
|
|
3
|
+
"version": "0.400.7",
|
|
4
|
+
"description": "analytics Backend",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "tsc"
|
|
8
|
+
},
|
|
9
|
+
"publishConfig": {
|
|
10
|
+
"directory": "dist",
|
|
11
|
+
"linkDirectory": true
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@nu-art/analytics-shared": "0.400.7",
|
|
15
|
+
"@nu-art/ts-common": "0.400.7",
|
|
16
|
+
"@nu-art/firebase-backend": "0.400.7",
|
|
17
|
+
"@nu-art/firebase-shared": "0.400.7",
|
|
18
|
+
"@nu-art/thunderstorm-backend": "0.400.7",
|
|
19
|
+
"@nu-art/thunderstorm-shared": "0.400.7",
|
|
20
|
+
"mixpanel": "~0.18.1"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {},
|
|
23
|
+
"private": false,
|
|
24
|
+
"unitConfig": {
|
|
25
|
+
"type": "typescript-lib"
|
|
26
|
+
},
|
|
27
|
+
"type": "module",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./index.d.ts",
|
|
31
|
+
"import": "./index.js"
|
|
32
|
+
},
|
|
33
|
+
"./*": {
|
|
34
|
+
"types": "./*.d.ts",
|
|
35
|
+
"import": "./*.js"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { Analytics_UpdateUser, TSAnalyticsEvent } from '@nu-art/analytics-shared';
|
|
2
|
+
import { Logger } from '@nu-art/ts-common';
|
|
3
|
+
import { AnalyticsPluginBaseConfig } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* C - plugin specific config, to be used later in interface implementations
|
|
6
|
+
* R - Translation response
|
|
7
|
+
*/
|
|
8
|
+
export declare abstract class AnalyticsPlugin_Base<R extends any = any, C extends AnalyticsPluginBaseConfig = AnalyticsPluginBaseConfig> extends Logger {
|
|
9
|
+
/**
|
|
10
|
+
* Unique key used to identify this plugin and retrieve its config.
|
|
11
|
+
* Example: 'mixpanel', 'amplitude', 'customLogger'
|
|
12
|
+
*/
|
|
13
|
+
abstract readonly key: string;
|
|
14
|
+
/**
|
|
15
|
+
* the config of the plugin, keeping important plugin specific data
|
|
16
|
+
* @private
|
|
17
|
+
*/
|
|
18
|
+
protected config: C | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* An event buffer, to packet the events into groups so they can later be bulk sent
|
|
21
|
+
* @private
|
|
22
|
+
*/
|
|
23
|
+
private eventBuffer;
|
|
24
|
+
/**
|
|
25
|
+
* A debouncer for the "Empty event buffer" functionality.
|
|
26
|
+
* @private
|
|
27
|
+
*/
|
|
28
|
+
private debounce_EmptyEventBuffer;
|
|
29
|
+
/**
|
|
30
|
+
* A queue for sending the events to the provider.
|
|
31
|
+
* @private
|
|
32
|
+
*/
|
|
33
|
+
private queue_Events;
|
|
34
|
+
protected abstract translateEvent(event: TSAnalyticsEvent): R;
|
|
35
|
+
protected abstract sendEvents: (events: R[]) => Promise<void>;
|
|
36
|
+
protected abstract updateUser_Impl: undefined | ((mode: Analytics_UpdateUser['request']['mode'], data: Analytics_UpdateUser['request']['userData']) => Promise<void>);
|
|
37
|
+
init(config: C): void;
|
|
38
|
+
private emptyEventBuffer;
|
|
39
|
+
registerEvent(event: TSAnalyticsEvent): void;
|
|
40
|
+
updateUser(request: Analytics_UpdateUser['request']): void;
|
|
41
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { debounce, Logger, LogLevel } from '@nu-art/ts-common';
|
|
2
|
+
import { QueueV2 } from '@nu-art/ts-common/utils/queue-v2';
|
|
3
|
+
/**
|
|
4
|
+
* C - plugin specific config, to be used later in interface implementations
|
|
5
|
+
* R - Translation response
|
|
6
|
+
*/
|
|
7
|
+
export class AnalyticsPlugin_Base extends Logger {
|
|
8
|
+
/**
|
|
9
|
+
* the config of the plugin, keeping important plugin specific data
|
|
10
|
+
* @private
|
|
11
|
+
*/
|
|
12
|
+
config;
|
|
13
|
+
/**
|
|
14
|
+
* An event buffer, to packet the events into groups so they can later be bulk sent
|
|
15
|
+
* @private
|
|
16
|
+
*/
|
|
17
|
+
eventBuffer = [];
|
|
18
|
+
/**
|
|
19
|
+
* A debouncer for the "Empty event buffer" functionality.
|
|
20
|
+
* @private
|
|
21
|
+
*/
|
|
22
|
+
debounce_EmptyEventBuffer = debounce(() => this.emptyEventBuffer(), 200, 2000);
|
|
23
|
+
/**
|
|
24
|
+
* A queue for sending the events to the provider.
|
|
25
|
+
* @private
|
|
26
|
+
*/
|
|
27
|
+
queue_Events = new QueueV2('plugin-events', e => this.sendEvents(e));
|
|
28
|
+
//######################### Initialization #########################
|
|
29
|
+
init(config) {
|
|
30
|
+
this.config = config;
|
|
31
|
+
const tag = `AnalyticsPlugin_${this.key}`;
|
|
32
|
+
this.setTag(tag);
|
|
33
|
+
this.setMinLevel(LogLevel.Info);
|
|
34
|
+
const message = `Loaded - ${this.config.active ? 'Active' : 'Inactive'}`;
|
|
35
|
+
this.logInfo(message);
|
|
36
|
+
}
|
|
37
|
+
//######################### Internal Logic #########################
|
|
38
|
+
emptyEventBuffer = () => {
|
|
39
|
+
if (this.eventBuffer.length === 0)
|
|
40
|
+
return;
|
|
41
|
+
this.queue_Events.addItem([...this.eventBuffer]);
|
|
42
|
+
this.eventBuffer = [];
|
|
43
|
+
};
|
|
44
|
+
//######################### Public Logic #########################
|
|
45
|
+
registerEvent(event) {
|
|
46
|
+
if (!this.config?.active)
|
|
47
|
+
return;
|
|
48
|
+
const translatedEvent = this.translateEvent(event);
|
|
49
|
+
this.eventBuffer.push(translatedEvent);
|
|
50
|
+
//If the max packet size has not been reached
|
|
51
|
+
if (this.eventBuffer.length < (this.config.eventPacketSize ?? 0))
|
|
52
|
+
return this.debounce_EmptyEventBuffer();
|
|
53
|
+
//Max packet size has been reached, empty the buffer now
|
|
54
|
+
this.emptyEventBuffer();
|
|
55
|
+
}
|
|
56
|
+
updateUser(request) {
|
|
57
|
+
if (!this.config?.active)
|
|
58
|
+
return;
|
|
59
|
+
this.updateUser_Impl?.(request.mode, request.userData);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { TSAnalyticsEvent } from '@nu-art/analytics-shared';
|
|
2
|
+
import { AnalyticsPlugin_Base } from './AnalyticsPlugin_Base.js';
|
|
3
|
+
export declare const pluginKey_Logger = "logger";
|
|
4
|
+
export declare class AnalyticsPlugin_Logger extends AnalyticsPlugin_Base {
|
|
5
|
+
key: string;
|
|
6
|
+
protected translateEvent(event: TSAnalyticsEvent): TSAnalyticsEvent;
|
|
7
|
+
protected updateUser_Impl: undefined;
|
|
8
|
+
protected sendEvents: (events: any[]) => Promise<void>;
|
|
9
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AnalyticsPlugin_Base } from './AnalyticsPlugin_Base.js';
|
|
2
|
+
export const pluginKey_Logger = 'logger';
|
|
3
|
+
export class AnalyticsPlugin_Logger extends AnalyticsPlugin_Base {
|
|
4
|
+
key = pluginKey_Logger;
|
|
5
|
+
translateEvent(event) {
|
|
6
|
+
return event;
|
|
7
|
+
}
|
|
8
|
+
updateUser_Impl = undefined;
|
|
9
|
+
sendEvents = async (events) => {
|
|
10
|
+
this.logInfoBold('######## Analytics Event - Start ########');
|
|
11
|
+
this.logInfo(events);
|
|
12
|
+
this.logInfoBold('######## Analytics Event - End ########');
|
|
13
|
+
};
|
|
14
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Analytics_UpdateUser, TSAnalyticsEvent } from '@nu-art/analytics-shared';
|
|
2
|
+
import { AnalyticsPlugin_Base } from './AnalyticsPlugin_Base.js';
|
|
3
|
+
import mixpanelLib from 'mixpanel';
|
|
4
|
+
import { TypedMap } from '@nu-art/ts-common';
|
|
5
|
+
import { AnalyticPanelConfig } from './types.js';
|
|
6
|
+
type MixedPanelsEventProperties = {
|
|
7
|
+
distinct_id: string;
|
|
8
|
+
time: number;
|
|
9
|
+
$session_id?: string;
|
|
10
|
+
$groups?: TypedMap<string>;
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
};
|
|
13
|
+
type MixedPanelsEvent = {
|
|
14
|
+
event: string;
|
|
15
|
+
properties: MixedPanelsEventProperties;
|
|
16
|
+
};
|
|
17
|
+
export declare const pluginKey_MixedPanels = "mixed-panels";
|
|
18
|
+
type MPConfig = AnalyticPanelConfig<{
|
|
19
|
+
mxConfig?: Partial<mixpanelLib.InitConfig>;
|
|
20
|
+
}>;
|
|
21
|
+
export declare class AnalyticsPlugin_MixedPanels extends AnalyticsPlugin_Base<MixedPanelsEvent, MPConfig> {
|
|
22
|
+
readonly key = "mixed-panels";
|
|
23
|
+
private mixpanel;
|
|
24
|
+
init(config: MPConfig): void;
|
|
25
|
+
private prepareUserProps;
|
|
26
|
+
protected translateEvent(event: TSAnalyticsEvent): MixedPanelsEvent;
|
|
27
|
+
protected sendEvents: (events: MixedPanelsEvent[]) => Promise<void>;
|
|
28
|
+
protected updateUser_Impl: (mode: Analytics_UpdateUser["request"]["mode"], data: Analytics_UpdateUser["request"]["userData"]) => Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { AnalyticsPlugin_Base } from './AnalyticsPlugin_Base.js';
|
|
2
|
+
import mixpanelLib from 'mixpanel';
|
|
3
|
+
import { _keys, BadImplementationException, exists, MissingDataException } from '@nu-art/ts-common';
|
|
4
|
+
export const pluginKey_MixedPanels = 'mixed-panels';
|
|
5
|
+
export class AnalyticsPlugin_MixedPanels extends AnalyticsPlugin_Base {
|
|
6
|
+
key = pluginKey_MixedPanels;
|
|
7
|
+
mixpanel;
|
|
8
|
+
init(config) {
|
|
9
|
+
super.init(config);
|
|
10
|
+
if (!config.token) {
|
|
11
|
+
if (config.active)
|
|
12
|
+
throw new MissingDataException(`Missing token for analytics plugin "${pluginKey_MixedPanels}"`);
|
|
13
|
+
else
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
this.mixpanel = mixpanelLib.init(config.token, config.mxConfig);
|
|
17
|
+
}
|
|
18
|
+
//######################### Internal Logic #########################
|
|
19
|
+
prepareUserProps = (data) => {
|
|
20
|
+
const { userId, ...rest } = data;
|
|
21
|
+
const props = {};
|
|
22
|
+
_keys(rest).forEach(key => {
|
|
23
|
+
switch (key) {
|
|
24
|
+
case 'firstName':
|
|
25
|
+
props.$first_name = rest[key];
|
|
26
|
+
break;
|
|
27
|
+
case 'lastName':
|
|
28
|
+
props.$last_name = rest[key];
|
|
29
|
+
break;
|
|
30
|
+
case 'displayName':
|
|
31
|
+
props.$name = rest[key];
|
|
32
|
+
break;
|
|
33
|
+
default:
|
|
34
|
+
props[key] = rest[key];
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
return props;
|
|
38
|
+
};
|
|
39
|
+
//######################### Implemented Logic #########################
|
|
40
|
+
translateEvent(event) {
|
|
41
|
+
return {
|
|
42
|
+
event: event.key,
|
|
43
|
+
properties: {
|
|
44
|
+
distinct_id: event.userId ?? 'unknown',
|
|
45
|
+
time: Math.floor(event.timestamp / 1000), //Mixed panels expects seconds
|
|
46
|
+
...(exists(event.context) ? event.context : {}),
|
|
47
|
+
...(exists(event.properties) ? event.properties : {}),
|
|
48
|
+
...(exists(event.groups) ? event.groups : {}),
|
|
49
|
+
...(exists(event.sessionId) ? { $session_id: event.sessionId } : {}),
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
sendEvents = async (events) => {
|
|
54
|
+
if (!this.mixpanel)
|
|
55
|
+
throw new BadImplementationException(`Calling send before analytics plugin ${pluginKey_MixedPanels} finished initializing`);
|
|
56
|
+
return new Promise((resolve, reject) => {
|
|
57
|
+
this.logDebug('Sending Events', events);
|
|
58
|
+
this.mixpanel.track_batch(events, {}, (errors) => {
|
|
59
|
+
if (errors?.length) {
|
|
60
|
+
this.logError(errors);
|
|
61
|
+
reject(errors);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
this.logDebug('Events Sent');
|
|
65
|
+
resolve();
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
};
|
|
70
|
+
updateUser_Impl = (mode, data) => {
|
|
71
|
+
if (!this.mixpanel)
|
|
72
|
+
throw new BadImplementationException(`Calling update user before analytics plugin ${pluginKey_MixedPanels} finished initializing`);
|
|
73
|
+
return new Promise((resolve, reject) => {
|
|
74
|
+
this.logDebug('Updating user', data);
|
|
75
|
+
const userProps = this.prepareUserProps(data);
|
|
76
|
+
const cb = (err) => {
|
|
77
|
+
if (err) {
|
|
78
|
+
this.logError(err);
|
|
79
|
+
reject(err);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
this.logDebug('Successfully updated user');
|
|
83
|
+
resolve();
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
switch (mode) {
|
|
87
|
+
case 'set':
|
|
88
|
+
this.mixpanel?.people.set(data.userId, userProps, cb);
|
|
89
|
+
break;
|
|
90
|
+
case 'set_once':
|
|
91
|
+
this.mixpanel?.people.set_once(data.userId, userProps, cb);
|
|
92
|
+
break;
|
|
93
|
+
default:
|
|
94
|
+
throw new BadImplementationException(`No Implementation for mode ${mode}`);
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
};
|
|
98
|
+
}
|
package/plugins/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { AnalyticsPlugin_Logger, pluginKey_Logger } from './AnalyticsPlugin_Logger.js';
|
|
2
|
+
import { AnalyticsPlugin_MixedPanels, pluginKey_MixedPanels } from './AnalyticsPlugin_MixedPanels.js';
|
|
3
|
+
export type AnalyticsPluginRegistry = {
|
|
4
|
+
[pluginKey_Logger]: AnalyticsPlugin_Logger;
|
|
5
|
+
[pluginKey_MixedPanels]: AnalyticsPlugin_MixedPanels;
|
|
6
|
+
};
|
package/plugins/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|