Skip to content

Commit 6c44e52

Browse files
sam-gcavolkovi
authored andcommitted
Add a cache layer to the event manager (#3808)
* Add caching of events to event manager * Formatting * Fix tests * Formatting
1 parent 2be9c66 commit 6c44e52

File tree

2 files changed

+82
-0
lines changed

2 files changed

+82
-0
lines changed

packages-exp/auth-exp/src/core/auth/auth_event_manager.test.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,4 +191,53 @@ describe('src/core/auth/auth_event_manager', () => {
191191
expect(consumer.onAuthEvent).to.have.been.calledWith(event);
192192
});
193193
});
194+
195+
context('caching', () => {
196+
let clock: sinon.SinonFakeTimers;
197+
198+
beforeEach(() => {
199+
clock = sinon.useFakeTimers();
200+
});
201+
202+
afterEach(() => {
203+
sinon.restore();
204+
});
205+
206+
it('only runs the event once for the consumer', () => {
207+
const consumer = makeConsumer(AuthEventType.LINK_VIA_POPUP);
208+
209+
const evt = makeEvent(AuthEventType.LINK_VIA_POPUP);
210+
manager.registerConsumer(consumer);
211+
manager.onEvent(evt);
212+
manager.onEvent(evt);
213+
214+
expect(consumer.onAuthEvent).to.have.been.calledOnce;
215+
});
216+
217+
it('clears the cache after ten minutes', () => {
218+
const consumer = makeConsumer(AuthEventType.LINK_VIA_POPUP);
219+
220+
const evt = makeEvent(AuthEventType.LINK_VIA_POPUP);
221+
manager.registerConsumer(consumer);
222+
manager.onEvent(evt);
223+
clock.tick(11 * 60 * 1000);
224+
manager.onEvent(evt);
225+
226+
expect(consumer.onAuthEvent).to.have.been.calledTwice;
227+
});
228+
229+
it('also caches stored redirects', () => {
230+
const consumer = makeConsumer([
231+
AuthEventType.SIGN_IN_VIA_REDIRECT,
232+
AuthEventType.LINK_VIA_REDIRECT,
233+
AuthEventType.REAUTH_VIA_REDIRECT,
234+
AuthEventType.UNKNOWN
235+
]);
236+
const event = makeEvent(AuthEventType.REAUTH_VIA_REDIRECT);
237+
manager.onEvent(event);
238+
manager.registerConsumer(consumer);
239+
manager.onEvent(event);
240+
expect(consumer.onAuthEvent).to.have.been.calledOnce;
241+
});
242+
});
194243
});

packages-exp/auth-exp/src/core/auth/auth_event_manager.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,16 @@ import {
2323
} from '../../model/popup_redirect';
2424
import { AUTH_ERROR_FACTORY, AuthErrorCode } from '../errors';
2525

26+
// The amount of time to store the UIDs of seen events; this is
27+
// set to 10 min by default
28+
const EVENT_DUPLICATION_CACHE_DURATION_MS = 10 * 60 * 1000;
29+
2630
export class AuthEventManager implements EventManager {
31+
private readonly cachedEventUids: Set<string> = new Set();
2732
private readonly consumers: Set<AuthEventConsumer> = new Set();
2833
private queuedRedirectEvent: AuthEvent | null = null;
2934
private hasHandledPotentialRedirect = false;
35+
private lastProcessedEventTime = Date.now();
3036

3137
constructor(private readonly appName: string) {}
3238

@@ -38,6 +44,7 @@ export class AuthEventManager implements EventManager {
3844
this.isEventForConsumer(this.queuedRedirectEvent, authEventConsumer)
3945
) {
4046
this.sendToConsumer(this.queuedRedirectEvent, authEventConsumer);
47+
this.saveEventToCache(this.queuedRedirectEvent);
4148
this.queuedRedirectEvent = null;
4249
}
4350
}
@@ -47,11 +54,17 @@ export class AuthEventManager implements EventManager {
4754
}
4855

4956
onEvent(event: AuthEvent): boolean {
57+
// Check if the event has already been handled
58+
if (this.hasEventBeenHandled(event)) {
59+
return false;
60+
}
61+
5062
let handled = false;
5163
this.consumers.forEach(consumer => {
5264
if (this.isEventForConsumer(event, consumer)) {
5365
handled = true;
5466
this.sendToConsumer(event, consumer);
67+
this.saveEventToCache(event);
5568
}
5669
});
5770

@@ -96,6 +109,26 @@ export class AuthEventManager implements EventManager {
96109
(!!event.eventId && event.eventId === consumer.eventId);
97110
return consumer.filter.includes(event.type) && eventIdMatches;
98111
}
112+
113+
private hasEventBeenHandled(event: AuthEvent): boolean {
114+
if (
115+
Date.now() - this.lastProcessedEventTime >=
116+
EVENT_DUPLICATION_CACHE_DURATION_MS
117+
) {
118+
this.cachedEventUids.clear();
119+
}
120+
121+
return this.cachedEventUids.has(eventUid(event));
122+
}
123+
124+
private saveEventToCache(event: AuthEvent): void {
125+
this.cachedEventUids.add(eventUid(event));
126+
this.lastProcessedEventTime = Date.now();
127+
}
128+
}
129+
130+
function eventUid(e: AuthEvent): string {
131+
return [e.type, e.eventId, e.sessionId, e.tenantId].filter(v => v).join('-');
99132
}
100133

101134
function isNullRedirectEvent({ type, error }: AuthEvent): boolean {

0 commit comments

Comments
 (0)