@chirpier/chirpier-js 0.1.2 → 0.1.4
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/README.md +15 -20
- package/dist/__tests__/chirpier.test.d.ts +2 -0
- package/dist/__tests__/chirpier.test.d.ts.map +1 -0
- package/dist/__tests__/chirpier.test.js +205 -0
- package/dist/__tests__/sdk.test.js +58 -121
- package/dist/constants.d.ts +5 -2
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +6 -3
- package/dist/index.d.ts +22 -10
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +174 -71
- package/package.json +7 -2
- package/src/__tests__/chirpier.test.ts +170 -0
- package/src/constants.ts +6 -3
- package/src/index.ts +147 -72
- package/src/__tests__/mocks/server.ts +0 -6
- package/src/__tests__/sdk.test.ts +0 -222
package/src/index.ts
CHANGED
|
@@ -1,30 +1,36 @@
|
|
|
1
1
|
// Import necessary dependencies
|
|
2
2
|
import axios, { AxiosInstance } from "axios";
|
|
3
3
|
import axiosRetry from "axios-retry";
|
|
4
|
-
import { v4 as uuidv4 } from "@lukeed/uuid";
|
|
5
4
|
import { Base64 } from "js-base64";
|
|
6
5
|
import {
|
|
7
6
|
DEFAULT_API_ENDPOINT,
|
|
8
7
|
DEFAULT_RETRIES,
|
|
9
8
|
DEFAULT_TIMEOUT,
|
|
9
|
+
DEFAULT_BATCH_SIZE,
|
|
10
|
+
DEFAULT_FLUSH_DELAY,
|
|
11
|
+
MAX_QUEUE_SIZE,
|
|
10
12
|
} from "./constants";
|
|
13
|
+
import AsyncLock from "async-lock";
|
|
14
|
+
|
|
15
|
+
// Define logging levels
|
|
16
|
+
export enum LogLevel {
|
|
17
|
+
None = 0,
|
|
18
|
+
Error = 1,
|
|
19
|
+
Info = 2,
|
|
20
|
+
Debug = 3,
|
|
21
|
+
}
|
|
11
22
|
|
|
12
23
|
// Define the options interface for Chirpier initialization
|
|
13
24
|
interface Options {
|
|
14
25
|
key: string;
|
|
15
|
-
|
|
16
|
-
retries?: number;
|
|
17
|
-
timeout?: number;
|
|
18
|
-
batchSize?: number;
|
|
19
|
-
flushInterval?: number;
|
|
26
|
+
logLevel?: LogLevel;
|
|
20
27
|
}
|
|
21
28
|
|
|
22
29
|
// Define the Event interface for monitoring
|
|
23
30
|
export interface Event {
|
|
24
31
|
group_id: string;
|
|
25
|
-
|
|
32
|
+
stream_name: string;
|
|
26
33
|
value: number;
|
|
27
|
-
event_id?: string;
|
|
28
34
|
}
|
|
29
35
|
|
|
30
36
|
// Custom error class for Chirpier-specific errors
|
|
@@ -36,41 +42,48 @@ export class ChirpierError extends Error {
|
|
|
36
42
|
}
|
|
37
43
|
}
|
|
38
44
|
|
|
45
|
+
interface QueuedEvent {
|
|
46
|
+
event: Event;
|
|
47
|
+
timestamp: number;
|
|
48
|
+
retryCount: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
39
51
|
/**
|
|
40
52
|
* Main Chirpier class for monitoring events.
|
|
41
53
|
*/
|
|
42
54
|
export class Chirpier {
|
|
55
|
+
private static instance: Chirpier | null = null;
|
|
43
56
|
private readonly apiKey: string;
|
|
44
57
|
private readonly apiEndpoint: string;
|
|
45
58
|
private readonly retries: number;
|
|
46
59
|
private readonly timeout: number;
|
|
47
60
|
private readonly axiosInstance: AxiosInstance;
|
|
48
|
-
private eventQueue:
|
|
49
|
-
private flushTimeout: NodeJS.Timeout | null = null;
|
|
61
|
+
private eventQueue: QueuedEvent[] = [];
|
|
50
62
|
private readonly batchSize: number;
|
|
51
|
-
private readonly
|
|
63
|
+
private readonly flushDelay: number;
|
|
64
|
+
private flushTimeoutId: NodeJS.Timeout | null = null;
|
|
65
|
+
private readonly queueLock = new AsyncLock();
|
|
66
|
+
private readonly flushLock = new AsyncLock();
|
|
67
|
+
private readonly logLevel: LogLevel;
|
|
52
68
|
|
|
53
69
|
/**
|
|
54
70
|
* Initializes a new instance of the Chirpier class.
|
|
55
71
|
* @param options - Configuration options for the SDK.
|
|
56
72
|
*/
|
|
57
|
-
constructor({
|
|
58
|
-
key,
|
|
59
|
-
|
|
60
|
-
retries = DEFAULT_RETRIES,
|
|
61
|
-
timeout = DEFAULT_TIMEOUT,
|
|
62
|
-
batchSize = 100,
|
|
63
|
-
flushInterval = 500,
|
|
64
|
-
}: Options) {
|
|
73
|
+
private constructor(options: Options) {
|
|
74
|
+
const { key, logLevel = LogLevel.None } = options;
|
|
75
|
+
|
|
65
76
|
if (!key || typeof key !== "string") {
|
|
66
77
|
throw new ChirpierError("API key is required and must be a string");
|
|
67
78
|
}
|
|
79
|
+
|
|
68
80
|
this.apiKey = key;
|
|
69
|
-
this.apiEndpoint =
|
|
70
|
-
this.retries =
|
|
71
|
-
this.timeout =
|
|
72
|
-
this.batchSize =
|
|
73
|
-
this.
|
|
81
|
+
this.apiEndpoint = DEFAULT_API_ENDPOINT;
|
|
82
|
+
this.retries = DEFAULT_RETRIES;
|
|
83
|
+
this.timeout = DEFAULT_TIMEOUT;
|
|
84
|
+
this.batchSize = DEFAULT_BATCH_SIZE;
|
|
85
|
+
this.flushDelay = DEFAULT_FLUSH_DELAY;
|
|
86
|
+
this.logLevel = logLevel;
|
|
74
87
|
|
|
75
88
|
// Create axios instance with authorization header
|
|
76
89
|
this.axiosInstance = axios.create({
|
|
@@ -102,6 +115,18 @@ export class Chirpier {
|
|
|
102
115
|
});
|
|
103
116
|
}
|
|
104
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Gets the singleton instance of Chirpier, creating it if it doesn't exist.
|
|
120
|
+
* @param options - Configuration options for the SDK.
|
|
121
|
+
* @returns The Chirpier instance.
|
|
122
|
+
*/
|
|
123
|
+
public static getInstance(options: Options): Chirpier | null {
|
|
124
|
+
if (!Chirpier.instance && options.key) {
|
|
125
|
+
Chirpier.instance = new Chirpier(options);
|
|
126
|
+
}
|
|
127
|
+
return Chirpier.instance;
|
|
128
|
+
}
|
|
129
|
+
|
|
105
130
|
/**
|
|
106
131
|
* Validates the event structure.
|
|
107
132
|
* @param event - The event to validate.
|
|
@@ -114,8 +139,8 @@ export class Chirpier {
|
|
|
114
139
|
event.group_id
|
|
115
140
|
) &&
|
|
116
141
|
event.group_id.trim().length > 0 &&
|
|
117
|
-
typeof event.
|
|
118
|
-
event.
|
|
142
|
+
typeof event.stream_name === "string" &&
|
|
143
|
+
event.stream_name.trim().length > 0 &&
|
|
119
144
|
typeof event.value === "number"
|
|
120
145
|
);
|
|
121
146
|
}
|
|
@@ -125,27 +150,26 @@ export class Chirpier {
|
|
|
125
150
|
* @param event - The event to monitor.
|
|
126
151
|
*/
|
|
127
152
|
public async monitor(event: Event): Promise<void> {
|
|
128
|
-
if (!this.apiKey) {
|
|
129
|
-
throw new ChirpierError("Chirpier SDK must be initialized before calling monitor()");
|
|
130
|
-
}
|
|
131
|
-
|
|
132
153
|
if (!this.isValidEvent(event)) {
|
|
133
154
|
throw new ChirpierError(
|
|
134
|
-
"Invalid event format. Must include group_id,
|
|
155
|
+
"Invalid event format. Must include group_id, stream_name, and numeric value."
|
|
135
156
|
);
|
|
136
157
|
}
|
|
137
158
|
|
|
138
|
-
|
|
139
|
-
|
|
159
|
+
await this.queueLock.acquire("queue", async () => {
|
|
160
|
+
if (this.eventQueue.length >= MAX_QUEUE_SIZE) {
|
|
161
|
+
throw new ChirpierError("Event queue is full.");
|
|
162
|
+
}
|
|
140
163
|
|
|
141
|
-
|
|
164
|
+
this.eventQueue.push({ event, timestamp: Date.now(), retryCount: 0 });
|
|
165
|
+
});
|
|
142
166
|
|
|
143
167
|
if (this.eventQueue.length >= this.batchSize) {
|
|
144
|
-
this.flushQueue();
|
|
145
|
-
} else if (!this.
|
|
146
|
-
this.
|
|
168
|
+
await this.flushQueue();
|
|
169
|
+
} else if (!this.flushTimeoutId) {
|
|
170
|
+
this.flushTimeoutId = setTimeout(
|
|
147
171
|
() => this.flushQueue(),
|
|
148
|
-
this.
|
|
172
|
+
this.flushDelay
|
|
149
173
|
);
|
|
150
174
|
}
|
|
151
175
|
}
|
|
@@ -154,32 +178,65 @@ export class Chirpier {
|
|
|
154
178
|
* Flushes the event queue by sending all events to the API.
|
|
155
179
|
*/
|
|
156
180
|
private async flushQueue(): Promise<void> {
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if (this.flushTimeout) {
|
|
162
|
-
clearTimeout(this.flushTimeout);
|
|
163
|
-
this.flushTimeout = null;
|
|
164
|
-
}
|
|
181
|
+
// Acquire the flush lock
|
|
182
|
+
await this.flushLock.acquire("flush", async () => {
|
|
183
|
+
let eventsToSend: QueuedEvent[] = [];
|
|
165
184
|
|
|
166
|
-
|
|
167
|
-
|
|
185
|
+
// Extract events from the queue under the queue lock
|
|
186
|
+
await this.queueLock.acquire("eventQueue", async () => {
|
|
187
|
+
if (this.eventQueue.length > 0) {
|
|
188
|
+
eventsToSend = [...this.eventQueue];
|
|
189
|
+
this.eventQueue = [];
|
|
190
|
+
}
|
|
191
|
+
});
|
|
168
192
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
} catch (error) {
|
|
173
|
-
console.error("Failed to send events:", error);
|
|
174
|
-
}
|
|
193
|
+
if (eventsToSend.length === 0) {
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
175
196
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
197
|
+
try {
|
|
198
|
+
// Clear any pending flush timeout
|
|
199
|
+
if (this.flushTimeoutId) {
|
|
200
|
+
clearTimeout(this.flushTimeoutId);
|
|
201
|
+
this.flushTimeoutId = null;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// Attempt to send events
|
|
205
|
+
await this.sendEvents(eventsToSend.map((qe) => qe.event));
|
|
206
|
+
|
|
207
|
+
if (this.logLevel >= LogLevel.Info) {
|
|
208
|
+
console.info(`Successfully sent ${eventsToSend.length} events`);
|
|
209
|
+
}
|
|
210
|
+
} catch (error) {
|
|
211
|
+
// Log failure
|
|
212
|
+
if (this.logLevel >= LogLevel.Error) {
|
|
213
|
+
console.error("Failed to send events:", error);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Requeue failed events with retry count checks
|
|
217
|
+
const retryableEvents: QueuedEvent[] = [];
|
|
218
|
+
for (const queuedEvent of eventsToSend) {
|
|
219
|
+
if (queuedEvent.retryCount >= this.retries) {
|
|
220
|
+
if (this.logLevel >= LogLevel.Error) {
|
|
221
|
+
console.error(
|
|
222
|
+
`Dropping event after ${this.retries} retries:`,
|
|
223
|
+
queuedEvent.event
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
continue; // Skip adding this event back to the queue
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Increment retry count and add back to the queue
|
|
230
|
+
queuedEvent.retryCount++;
|
|
231
|
+
retryableEvents.push(queuedEvent);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// Requeue remaining retryable events
|
|
235
|
+
await this.queueLock.acquire("eventQueue", async () => {
|
|
236
|
+
this.eventQueue = [...retryableEvents, ...this.eventQueue];
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
});
|
|
183
240
|
}
|
|
184
241
|
|
|
185
242
|
/**
|
|
@@ -189,6 +246,21 @@ export class Chirpier {
|
|
|
189
246
|
private async sendEvents(events: Event[]): Promise<void> {
|
|
190
247
|
await this.axiosInstance.post(this.apiEndpoint, events);
|
|
191
248
|
}
|
|
249
|
+
|
|
250
|
+
// Stop the timeout and uninitialize the Chirpier instance
|
|
251
|
+
public static async stop(): Promise<void> {
|
|
252
|
+
if (!Chirpier.instance) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
if (Chirpier.instance.flushTimeoutId) {
|
|
256
|
+
clearTimeout(Chirpier.instance.flushTimeoutId);
|
|
257
|
+
Chirpier.instance.flushTimeoutId = null;
|
|
258
|
+
}
|
|
259
|
+
// Flush any remaining events in the queue
|
|
260
|
+
await Chirpier.instance.flushQueue();
|
|
261
|
+
// Uninitialize the Chirpier instance
|
|
262
|
+
Chirpier.instance = null;
|
|
263
|
+
}
|
|
192
264
|
}
|
|
193
265
|
|
|
194
266
|
/**
|
|
@@ -226,9 +298,6 @@ function isValidJWT(token: string): boolean {
|
|
|
226
298
|
}
|
|
227
299
|
}
|
|
228
300
|
|
|
229
|
-
// Singleton instance of Chirpier
|
|
230
|
-
let chirpierInstance: Chirpier | null = null;
|
|
231
|
-
|
|
232
301
|
/**
|
|
233
302
|
* Initializes the Chirpier SDK.
|
|
234
303
|
* @param options - Configuration options for the SDK.
|
|
@@ -239,15 +308,19 @@ export function initialize(options: Options): void {
|
|
|
239
308
|
}
|
|
240
309
|
|
|
241
310
|
try {
|
|
242
|
-
|
|
311
|
+
Chirpier.getInstance(options);
|
|
243
312
|
} catch (error) {
|
|
244
313
|
if (error instanceof ChirpierError) {
|
|
245
|
-
|
|
314
|
+
if (options.logLevel && options.logLevel >= LogLevel.Error) {
|
|
315
|
+
console.error("Failed to initialize Chirpier SDK:", error.message);
|
|
316
|
+
}
|
|
246
317
|
} else {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
318
|
+
if (options.logLevel && options.logLevel >= LogLevel.Error) {
|
|
319
|
+
console.error(
|
|
320
|
+
"An unexpected error occurred during Chirpier SDK initialization:",
|
|
321
|
+
error
|
|
322
|
+
);
|
|
323
|
+
}
|
|
251
324
|
}
|
|
252
325
|
throw error;
|
|
253
326
|
}
|
|
@@ -258,12 +331,14 @@ export function initialize(options: Options): void {
|
|
|
258
331
|
* @param event - The event to monitor.
|
|
259
332
|
*/
|
|
260
333
|
export function monitor(event: Event): void {
|
|
261
|
-
|
|
334
|
+
const instance = Chirpier.getInstance({} as Options);
|
|
335
|
+
if (!instance) {
|
|
262
336
|
throw new ChirpierError(
|
|
263
337
|
"Chirpier SDK is not initialized. Please call initialize() first."
|
|
264
338
|
);
|
|
265
339
|
}
|
|
266
|
-
|
|
340
|
+
|
|
341
|
+
instance.monitor(event).catch((error) => {
|
|
267
342
|
console.error("Error in monitor function:", error);
|
|
268
343
|
});
|
|
269
344
|
}
|
|
@@ -1,222 +0,0 @@
|
|
|
1
|
-
import { Chirpier, ChirpierError, Event, initialize, monitor } from "../index";
|
|
2
|
-
import { DEFAULT_API_ENDPOINT, DEFAULT_RETRIES } from "../constants";
|
|
3
|
-
import MockAdapter from "axios-mock-adapter";
|
|
4
|
-
import axios from "axios";
|
|
5
|
-
import { cleanupMockServer } from "./mocks/server";
|
|
6
|
-
import { v4 as uuidv4 } from "@lukeed/uuid";
|
|
7
|
-
|
|
8
|
-
jest.mock("@lukeed/uuid");
|
|
9
|
-
|
|
10
|
-
describe("Chirpier SDK", () => {
|
|
11
|
-
let chirpier: Chirpier;
|
|
12
|
-
|
|
13
|
-
afterEach(() => {
|
|
14
|
-
// Clean up mock server
|
|
15
|
-
cleanupMockServer();
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
describe("Initialization", () => {
|
|
19
|
-
test("should initialize with default values", () => {
|
|
20
|
-
chirpier = new Chirpier({
|
|
21
|
-
key: "api_key",
|
|
22
|
-
});
|
|
23
|
-
|
|
24
|
-
// Setup mock server
|
|
25
|
-
const mock = new MockAdapter(axios);
|
|
26
|
-
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
27
|
-
|
|
28
|
-
expect(chirpier["apiEndpoint"]).toBe(DEFAULT_API_ENDPOINT);
|
|
29
|
-
expect(chirpier["retries"]).toBe(DEFAULT_RETRIES);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test("should initialize with custom values using mock server", () => {
|
|
33
|
-
chirpier = new Chirpier({
|
|
34
|
-
key: "api_key",
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
// Setup mock server
|
|
38
|
-
const mock = new MockAdapter(axios);
|
|
39
|
-
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
40
|
-
|
|
41
|
-
const customChirpier = new Chirpier({
|
|
42
|
-
key: "api_key",
|
|
43
|
-
apiEndpoint: DEFAULT_API_ENDPOINT,
|
|
44
|
-
retries: 5,
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
expect(customChirpier["apiEndpoint"]).toBe(DEFAULT_API_ENDPOINT);
|
|
48
|
-
expect(customChirpier["retries"]).toBe(5);
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test("should throw error if key is not provided", () => {
|
|
52
|
-
chirpier = new Chirpier({
|
|
53
|
-
key: "api_key",
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
// Setup mock server
|
|
57
|
-
const mock = new MockAdapter(axios);
|
|
58
|
-
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
59
|
-
|
|
60
|
-
expect(() => new Chirpier({} as any)).toThrow(ChirpierError);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test("should throw error if key is not a valid JWT", () => {
|
|
64
|
-
expect(() => initialize({ key: "invalid_key" })).toThrow(ChirpierError);
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
test("should initialize successfully with a valid JWT", () => {
|
|
68
|
-
const validJWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
69
|
-
expect(() => initialize({ key: validJWT })).not.toThrow();
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
describe("monitor", () => {
|
|
74
|
-
test("event should be sent", async () => {
|
|
75
|
-
chirpier = new Chirpier({
|
|
76
|
-
key: "api_key",
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
// Setup mock server
|
|
80
|
-
const mock = new MockAdapter(axios);
|
|
81
|
-
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
82
|
-
|
|
83
|
-
const validJWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
84
|
-
initialize({ key: validJWT });
|
|
85
|
-
|
|
86
|
-
const event: Event = {
|
|
87
|
-
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
88
|
-
stream: "test-stream",
|
|
89
|
-
value: 1,
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
await chirpier.monitor(event);
|
|
93
|
-
|
|
94
|
-
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for flush
|
|
95
|
-
|
|
96
|
-
expect(mock.history.post.length).toBe(1);
|
|
97
|
-
expect(mock.history.post[0].url).toBe(DEFAULT_API_ENDPOINT);
|
|
98
|
-
expect(JSON.parse(mock.history.post[0].data)).toEqual([
|
|
99
|
-
{
|
|
100
|
-
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
101
|
-
stream: "test-stream",
|
|
102
|
-
value: 1,
|
|
103
|
-
},
|
|
104
|
-
]);
|
|
105
|
-
|
|
106
|
-
// Clean up the mock
|
|
107
|
-
mock.reset();
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
test("should throw error for invalid event", async () => {
|
|
111
|
-
chirpier = new Chirpier({
|
|
112
|
-
key: "api_key",
|
|
113
|
-
});
|
|
114
|
-
const invalidEvent = {
|
|
115
|
-
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
116
|
-
} as any;
|
|
117
|
-
await expect(chirpier.monitor(invalidEvent)).rejects.toThrow(
|
|
118
|
-
ChirpierError
|
|
119
|
-
);
|
|
120
|
-
|
|
121
|
-
// Clean up the mock
|
|
122
|
-
const mock = new MockAdapter(axios);
|
|
123
|
-
mock.reset();
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
test("should batch events and flush when batch size is reached", async () => {
|
|
127
|
-
const mock = new MockAdapter(axios);
|
|
128
|
-
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
129
|
-
|
|
130
|
-
const validJWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
131
|
-
initialize({ key: validJWT, batchSize: 2 });
|
|
132
|
-
|
|
133
|
-
const event: Event = {
|
|
134
|
-
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
135
|
-
stream: "test-stream",
|
|
136
|
-
value: 1,
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
monitor(event);
|
|
140
|
-
monitor(event);
|
|
141
|
-
|
|
142
|
-
await new Promise(resolve => setTimeout(resolve, 100)); // Wait for flush
|
|
143
|
-
|
|
144
|
-
expect(mock.history.post.length).toBe(1);
|
|
145
|
-
expect(JSON.parse(mock.history.post[0].data).length).toBe(2);
|
|
146
|
-
|
|
147
|
-
mock.reset();
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
test("should flush events after interval", async () => {
|
|
151
|
-
const mock = new MockAdapter(axios);
|
|
152
|
-
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
153
|
-
|
|
154
|
-
const validJWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
155
|
-
initialize({ key: validJWT, flushInterval: 100 });
|
|
156
|
-
|
|
157
|
-
const event: Event = {
|
|
158
|
-
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
159
|
-
stream: "test-stream",
|
|
160
|
-
value: 1,
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
monitor(event);
|
|
164
|
-
|
|
165
|
-
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for flush
|
|
166
|
-
|
|
167
|
-
expect(mock.history.post.length).toBe(1);
|
|
168
|
-
expect(JSON.parse(mock.history.post[0].data).length).toBe(1);
|
|
169
|
-
|
|
170
|
-
mock.reset();
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
test("should use provided event_id if available", async () => {
|
|
174
|
-
const mock = new MockAdapter(axios);
|
|
175
|
-
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
176
|
-
|
|
177
|
-
const validJWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
178
|
-
initialize({ key: validJWT });
|
|
179
|
-
|
|
180
|
-
const event: Event = {
|
|
181
|
-
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
182
|
-
stream: "test-stream",
|
|
183
|
-
value: 1,
|
|
184
|
-
event_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
185
|
-
};
|
|
186
|
-
|
|
187
|
-
monitor(event);
|
|
188
|
-
|
|
189
|
-
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for flush
|
|
190
|
-
|
|
191
|
-
expect(mock.history.post.length).toBe(1);
|
|
192
|
-
expect(JSON.parse(mock.history.post[0].data)[0].event_id).toBe("f3438ee9-b964-48aa-b938-a803df440a3c");
|
|
193
|
-
|
|
194
|
-
mock.reset();
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
test("should generate event_id if not provided", async () => {
|
|
198
|
-
const mock = new MockAdapter(axios);
|
|
199
|
-
mock.onPost(DEFAULT_API_ENDPOINT).reply(200, { success: true });
|
|
200
|
-
|
|
201
|
-
(uuidv4 as jest.Mock).mockReturnValue("generated-uuid");
|
|
202
|
-
|
|
203
|
-
const validJWT = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c";
|
|
204
|
-
initialize({ key: validJWT });
|
|
205
|
-
|
|
206
|
-
const event: Event = {
|
|
207
|
-
group_id: "f3438ee9-b964-48aa-b938-a803df440a3c",
|
|
208
|
-
stream: "test-stream",
|
|
209
|
-
value: 1,
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
monitor(event);
|
|
213
|
-
|
|
214
|
-
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait for flush
|
|
215
|
-
|
|
216
|
-
expect(mock.history.post.length).toBe(1);
|
|
217
|
-
expect(JSON.parse(mock.history.post[0].data)[0].event_id).toBe("generated-uuid");
|
|
218
|
-
|
|
219
|
-
mock.reset();
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
});
|