Skip to content

Commit

Permalink
chore(events): Update event message format, bump tsc (#162)
Browse files Browse the repository at this point in the history
  • Loading branch information
felipecsl authored Dec 9, 2024
1 parent 99b5b46 commit 35a7269
Show file tree
Hide file tree
Showing 7 changed files with 93 additions and 27 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@
"ts-jest": "^29.1.1",
"ts-loader": "^9.3.1",
"ts-node": "^10.9.1",
"typescript": "^4.7.4",
"typescript": "^5.7.2",
"webpack": "^5.73.0",
"webpack-cli": "^4.10.0"
},
Expand Down
4 changes: 3 additions & 1 deletion src/cache/lru-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,9 @@ export class LRUCache implements Map<string, string> {
// Therefore, the first key represents the oldest entry, which is the least recently used item in our cache.
// We use Map.prototype.keys().next().value to obtain this oldest key and then delete it from the cache.
const oldestKey = this.cache.keys().next().value;
this.delete(oldestKey);
if (oldestKey) {
this.delete(oldestKey);
}
}

this.cache.set(key, value);
Expand Down
15 changes: 11 additions & 4 deletions src/client/eppo-client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -975,10 +975,17 @@ export default class EppoClient {
return result;
}

/** TODO */
// noinspection JSUnusedGlobalSymbols
track(event: unknown, params: Record<string, unknown>) {
this.eventDispatcher.dispatch({ id: randomUUID(), data: event, params });
/**
* Enqueues an arbitrary event. Events must have a type and a payload.
* TODO: enforce max message size
*/
track(type: string, event: unknown) {
this.eventDispatcher.dispatch({
uuid: randomUUID(),
type,
timestamp: new Date().getTime(),
payload: event,
});
}

private newFlagEvaluationDetailsBuilder(flagKey: string): FlagEvaluationDetailsBuilder {
Expand Down
83 changes: 69 additions & 14 deletions src/events/default-event-dispatcher.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,24 @@ describe('DefaultEventDispatcher', () => {
const { dispatcher, batchProcessor } = createDispatcher();

// Add three events to the queue
dispatcher.dispatch({ id: 'foo-1', data: 'event1', params: {} });
dispatcher.dispatch({ id: 'foo-2', data: 'event2', params: {} });
dispatcher.dispatch({ id: 'foo-3', data: 'event3', params: {} });
dispatcher.dispatch({
uuid: 'foo-1',
payload: 'event1',
timestamp: new Date().getTime(),
type: 'foo',
});
dispatcher.dispatch({
uuid: 'foo-2',
payload: 'event2',
timestamp: new Date().getTime(),
type: 'foo',
});
dispatcher.dispatch({
uuid: 'foo-3',
payload: 'event3',
timestamp: new Date().getTime(),
type: 'foo',
});

const batch1 = batchProcessor.nextBatch();
expect(batch1).toHaveLength(2);
Expand All @@ -66,9 +81,24 @@ describe('DefaultEventDispatcher', () => {
describe('deliverNextBatch', () => {
it('delivers the next batch of events using fetch', async () => {
const { dispatcher } = createDispatcher();
dispatcher.dispatch({ id: 'foo-1', data: 'event1', params: {} });
dispatcher.dispatch({ id: 'foo-2', data: 'event2', params: {} });
dispatcher.dispatch({ id: 'foo-3', data: 'event3', params: {} });
dispatcher.dispatch({
uuid: 'foo-1',
payload: 'event1',
timestamp: new Date().getTime(),
type: 'foo',
});
dispatcher.dispatch({
uuid: 'foo-2',
payload: 'event2',
timestamp: new Date().getTime(),
type: 'foo',
});
dispatcher.dispatch({
uuid: 'foo-3',
payload: 'event3',
timestamp: new Date().getTime(),
type: 'foo',
});

const fetch = global.fetch as jest.Mock;
fetch.mockResolvedValue({ ok: true });
Expand All @@ -86,8 +116,8 @@ describe('DefaultEventDispatcher', () => {
let fetchOptions = fetch.mock.calls[0][1];
let payload = JSON.parse(fetchOptions.body);
expect(payload).toEqual([
expect.objectContaining({ data: 'event1' }),
expect.objectContaining({ data: 'event2' }),
expect.objectContaining({ payload: 'event1' }),
expect.objectContaining({ payload: 'event2' }),
]);

await new Promise((resolve) => setTimeout(resolve, 100));
Expand All @@ -102,7 +132,7 @@ describe('DefaultEventDispatcher', () => {

fetchOptions = fetch.mock.calls[1][1];
payload = JSON.parse(fetchOptions.body);
expect(payload).toEqual([expect.objectContaining({ data: 'event3' })]);
expect(payload).toEqual([expect.objectContaining({ payload: 'event3' })]);
});

it('does not schedule delivery if the queue is empty', async () => {
Expand All @@ -117,7 +147,12 @@ describe('DefaultEventDispatcher', () => {
describe('retry logic', () => {
it('retries failed deliveries after the retry interval', async () => {
const { dispatcher } = createDispatcher();
dispatcher.dispatch({ id: 'foo', data: 'event1' });
dispatcher.dispatch({
uuid: 'foo',
payload: 'event1',
timestamp: new Date().getTime(),
type: 'foo',
});

// Simulate fetch failure on the first attempt
(global.fetch as jest.Mock)
Expand Down Expand Up @@ -151,8 +186,18 @@ describe('DefaultEventDispatcher', () => {
triggerNetworkStatusChange: () => cb(isOffline),
};
const { dispatcher } = createDispatcher({ networkStatusListener });
dispatcher.dispatch({ id: '1', data: 'event1', params: {} });
dispatcher.dispatch({ id: '2', data: 'event2', params: {} });
dispatcher.dispatch({
uuid: '1',
payload: 'event1',
timestamp: new Date().getTime(),
type: 'foo',
});
dispatcher.dispatch({
uuid: '2',
payload: 'event2',
timestamp: new Date().getTime(),
type: 'foo',
});

isOffline = true;
// simulate the network going offline
Expand All @@ -175,8 +220,18 @@ describe('DefaultEventDispatcher', () => {
triggerNetworkStatusChange: () => cb(isOffline),
};
const { dispatcher } = createDispatcher({ networkStatusListener });
dispatcher.dispatch({ id: '1', data: 'event1', params: {} });
dispatcher.dispatch({ id: '2', data: 'event2', params: {} });
dispatcher.dispatch({
uuid: '1',
payload: 'event1',
timestamp: new Date().getTime(),
type: 'foo',
});
dispatcher.dispatch({
uuid: '2',
payload: 'event2',
timestamp: new Date().getTime(),
type: 'foo',
});

const fetch = global.fetch as jest.Mock;
fetch.mockResolvedValue({ ok: true });
Expand Down
1 change: 1 addition & 0 deletions src/events/event-delivery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export default class EventDelivery {
// TODO: Figure out proper request body encoding format for batch, using JSON for now
body: JSON.stringify(batch),
});
// TODO: Parse response to check `failed_event_uploads` for any failed event ingestions in the batch
return response.ok;
} catch {
logger.warn('Failed to upload event batch');
Expand Down
7 changes: 4 additions & 3 deletions src/events/event-dispatcher.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
export type Event = {
id: string;
data: unknown;
params?: Record<string, unknown>;
uuid: string;
timestamp: number;
type: string;
payload: unknown;
};

export default interface EventDispatcher {
Expand Down
8 changes: 4 additions & 4 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -4112,10 +4112,10 @@ type-fest@^0.21.3:
resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz"
integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==

typescript@^4.7.4:
version "4.7.4"
resolved "https://registry.npmjs.org/typescript/-/typescript-4.7.4.tgz"
integrity sha512-C0WQT0gezHuw6AdY1M2jxUO83Rjf0HP7Sk1DtXj6j1EwkQNZrHAg2XPWlq62oqEhYvONq5pkC2Y9oPljWToLmQ==
typescript@^5.7.2:
version "5.7.2"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.7.2.tgz#3169cf8c4c8a828cde53ba9ecb3d2b1d5dd67be6"
integrity sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==

unbox-primitive@^1.0.2:
version "1.0.2"
Expand Down

0 comments on commit 35a7269

Please sign in to comment.