Skip to content

Commit 2092b43

Browse files
committed
processor management
1 parent 1faced4 commit 2092b43

File tree

2 files changed

+84
-32
lines changed

2 files changed

+84
-32
lines changed

src/modules/analytics.ts

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -68,17 +68,6 @@ export const createAnalyticsModule = ({
6868
return analyticsSharedState.sessionContext;
6969
};
7070

71-
const track = (params: TrackEventParams) => {
72-
if (!enabled || analyticsSharedState.requestsQueue.length >= maxQueueSize) {
73-
return;
74-
}
75-
const intrinsicData = getEventIntrinsicData();
76-
analyticsSharedState.requestsQueue.push({
77-
...params,
78-
...intrinsicData,
79-
});
80-
};
81-
8271
const batchRequestFallback = async (events: AnalyticsApiRequestData[]) => {
8372
await axiosClient.request({
8473
method: "POST",
@@ -131,20 +120,35 @@ export const createAnalyticsModule = ({
131120
window.addEventListener("visibilitychange", onVisibilityChange);
132121
}
133122

134-
// start analytics processor only if it's the first instance and analytics is enabled //
135-
if (enabled) {
123+
const startProcessing = () => {
124+
if (!enabled) return;
136125
startAnalyticsProcessor(flush, {
137126
throttleTime,
138127
batchSize,
139128
});
140-
}
129+
};
130+
131+
const track = (params: TrackEventParams) => {
132+
if (!enabled || analyticsSharedState.requestsQueue.length >= maxQueueSize) {
133+
return;
134+
}
135+
const intrinsicData = getEventIntrinsicData();
136+
analyticsSharedState.requestsQueue.push({
137+
...params,
138+
...intrinsicData,
139+
});
140+
startProcessing();
141+
};
141142

142143
const cleanup = () => {
143144
if (typeof window === "undefined") return;
144145
window.removeEventListener("visibilitychange", onVisibilityChange);
145146
stopAnalyticsProcessor();
146147
};
147148

149+
// start the flusing process ///
150+
startProcessing();
151+
148152
return {
149153
track,
150154
cleanup,
@@ -163,11 +167,16 @@ async function startAnalyticsProcessor(
163167
}
164168
) {
165169
if (analyticsSharedState.isProcessing) {
170+
// only one instance of the analytics processor can be running at a time //
166171
return;
167172
}
168173
analyticsSharedState.isProcessing = true;
174+
169175
const { throttleTime = 1000, batchSize = 30 } = options ?? {};
170-
while (analyticsSharedState.isProcessing) {
176+
while (
177+
analyticsSharedState.isProcessing &&
178+
analyticsSharedState.requestsQueue.length > 0
179+
) {
171180
const requests = analyticsSharedState.requestsQueue.splice(0, batchSize);
172181
if (requests.length > 0) {
173182
try {
@@ -179,6 +188,7 @@ async function startAnalyticsProcessor(
179188
}
180189
await new Promise((resolve) => setTimeout(resolve, throttleTime));
181190
}
191+
analyticsSharedState.isProcessing = false;
182192
}
183193

184194
function getEventIntrinsicData(): TrackEventIntrinsicData {

tests/unit/analytics.test.ts

Lines changed: 59 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ import {
66
TrackEventData,
77
} from "../../src/index.ts";
88
import { getSharedInstance } from "../../src/utils/sharedInstance.ts";
9+
import { User } from "../../src/modules/auth.types.ts";
10+
import { AxiosInstance } from "axios";
911

1012
describe("Analytics Module", () => {
1113
let base44: ReturnType<typeof createClient>;
@@ -19,26 +21,45 @@ describe("Analytics Module", () => {
1921
const serverUrl = "https://api.base44.com";
2022

2123
beforeEach(() => {
22-
base44 = createClient({
23-
serverUrl,
24-
appId,
25-
});
24+
vi.mock("../../src/utils/axios-client.ts", () => ({
25+
createAxiosClient: vi.fn().mockImplementation(
26+
() =>
27+
({
28+
request: vi.fn().mockResolvedValue({
29+
status: 200,
30+
data: {
31+
message: "success",
32+
},
33+
}),
34+
} as unknown as AxiosInstance)
35+
),
36+
}));
2637
sharedState = getSharedInstance("analytics", () => ({
2738
requestsQueue: [],
2839
isProcessing: false,
29-
sessionContext: {
30-
user_id: "test-user-id",
31-
},
32-
config: {
33-
enabled: true,
34-
maxQueueSize: 1000,
35-
throttleTime: 1000,
36-
batchSize: 30,
37-
},
40+
sessionContext: {},
41+
config: {},
3842
}));
43+
sharedState.isProcessing = false;
44+
sharedState.requestsQueue = [];
45+
sharedState.sessionContext = {
46+
user_id: "test-user-id",
47+
};
48+
sharedState.config = {
49+
enabled: true,
50+
maxQueueSize: 1000,
51+
throttleTime: 1000,
52+
batchSize: 2,
53+
};
54+
55+
base44 = createClient({
56+
serverUrl,
57+
appId,
58+
});
3959
});
4060

4161
afterEach(() => {
62+
vi.clearAllMocks();
4263
base44.cleanup();
4364
sharedState = null;
4465
});
@@ -47,17 +68,38 @@ describe("Analytics Module", () => {
4768
expect(base44.analytics).toBeDefined();
4869
expect(sharedState).toBeDefined();
4970
expect(sharedState?.requestsQueue).toBeDefined();
50-
expect(sharedState?.isProcessing).toBe(true);
71+
expect(sharedState?.isProcessing).toBe(false);
5172
});
5273

5374
test("should track an event", () => {
54-
vi.spyOn(base44.analytics, "track").mockImplementation(() => {
55-
console.log("track called");
56-
});
75+
vi.spyOn(base44.analytics, "track");
5776

5877
base44.analytics.track({ eventName: "test-event" });
78+
expect(sharedState?.isProcessing).toBe(true);
5979
expect(base44.analytics.track).toHaveBeenCalledWith({
6080
eventName: "test-event",
6181
});
6282
});
83+
84+
test("should track multiple events", async () => {
85+
vi.useFakeTimers();
86+
87+
for (let i = 0; i < 5; i++) {
88+
base44.analytics.track({ eventName: `test-event ${i}` });
89+
}
90+
91+
expect(sharedState?.isProcessing).toBe(true);
92+
expect(sharedState?.requestsQueue.length).toBe(4);
93+
await vi.advanceTimersByTimeAsync(1000);
94+
expect(sharedState?.requestsQueue.length).toBe(2);
95+
// add another event while processing to mix things up
96+
base44.analytics.track({ eventName: `test-event 5` });
97+
98+
await vi.advanceTimersByTimeAsync(1000);
99+
expect(sharedState?.requestsQueue.length).toBe(1);
100+
await vi.advanceTimersByTimeAsync(1000);
101+
expect(sharedState?.requestsQueue.length).toBe(0);
102+
await vi.advanceTimersByTimeAsync(1000);
103+
expect(sharedState?.isProcessing).toBe(false);
104+
});
63105
});

0 commit comments

Comments
 (0)