From 2ff2a9a01fb2745e9c9c11e70f6e8865e9a54485 Mon Sep 17 00:00:00 2001 From: Billy Date: Tue, 5 Sep 2023 11:23:07 -0700 Subject: [PATCH 01/28] chore: create event bus --- src/event-bus/EventBus.ts | 37 +++++++++++++++++++++++ src/event-bus/__tests__/EventBus.test.ts | 38 ++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 src/event-bus/EventBus.ts create mode 100644 src/event-bus/__tests__/EventBus.test.ts diff --git a/src/event-bus/EventBus.ts b/src/event-bus/EventBus.ts new file mode 100644 index 00000000..9d12a2d5 --- /dev/null +++ b/src/event-bus/EventBus.ts @@ -0,0 +1,37 @@ +export type Subscriber = (payload: any) => void; + +/** A topic-based event bus to facilitate communication between plugins */ +export class EventBus { + // map + private subscribers = new Map(); + + subscribe(topic: string, subscriber: Subscriber): void { + const list = this.subscribers.get(topic) ?? []; + if (list.length === 0) { + this.subscribers.set(topic, list); + } + list.push(subscriber); + } + + unsubscribe(topic: string, subscriber: Subscriber) { + const list = this.subscribers.get(topic); + if (list) { + for (let i = 0; i < list.length; i++) { + if (list[i] === subscriber) { + list.splice(i, 1); + return true; + } + } + } + return false; + } + + notify(topic: string, payload: any): void { + const list = this.subscribers.get(topic); + if (list) { + for (const subscriber of list) { + subscriber(payload); + } + } + } +} diff --git a/src/event-bus/__tests__/EventBus.test.ts b/src/event-bus/__tests__/EventBus.test.ts new file mode 100644 index 00000000..80e8d186 --- /dev/null +++ b/src/event-bus/__tests__/EventBus.test.ts @@ -0,0 +1,38 @@ +import { EventBus } from '../EventBus'; + +describe('EventBus tests', () => { + let eventBus: EventBus; + const l1 = jest.fn(); + const l2 = jest.fn(); + beforeEach(() => { + eventBus = new EventBus(); + jest.clearAllMocks(); + }); + test('when notify is invoked then all listeners are called', async () => { + // init + eventBus.subscribe('food', l1); + eventBus.subscribe('food', l2); + + // run + eventBus.notify('food', 'burger'); + + // assert + expect(l1).toHaveBeenCalledWith('burger'); + expect(l2).toHaveBeenCalledWith('burger'); + }); + + test('when listener is removed then it is not called', async () => { + // init + eventBus.subscribe('food', l1); + eventBus.subscribe('food', l2); + const removed = eventBus.unsubscribe('food', l2); + + // run + eventBus.notify('food', 'burger'); + + // assert + expect(l1).toHaveBeenCalledWith('burger'); + expect(removed).toBe(true); + expect(l2).not.toHaveBeenCalled(); + }); +}); From b7655019ddd281623b3067252e2561394a14e2b1 Mon Sep 17 00:00:00 2001 From: Billy Date: Sat, 9 Sep 2023 09:37:31 -0700 Subject: [PATCH 02/28] chore: add EventBus to Orchestration --- src/event-bus/EventBus.ts | 12 ++--- src/event-bus/__tests__/EventBus.test.ts | 2 +- src/event-cache/EventCache.ts | 13 +++-- src/event-cache/__tests__/EventCache.test.ts | 49 +++++++++++++++++++ src/orchestration/Orchestration.ts | 5 +- .../__tests__/FetchPlugin.test.ts | 26 +++------- .../__tests__/ResourcePlugin.test.ts | 14 ++---- .../event-plugins/__tests__/XhrPlugin.test.ts | 21 ++------ src/plugins/types.ts | 3 +- src/test-utils/test-utils.ts | 25 ++++------ 10 files changed, 99 insertions(+), 71 deletions(-) diff --git a/src/event-bus/EventBus.ts b/src/event-bus/EventBus.ts index 9d12a2d5..a63fac03 100644 --- a/src/event-bus/EventBus.ts +++ b/src/event-bus/EventBus.ts @@ -1,11 +1,11 @@ -export type Subscriber = (payload: any) => void; +export type Subsriber = (payload: any) => void; /** A topic-based event bus to facilitate communication between plugins */ -export class EventBus { - // map - private subscribers = new Map(); +export default class EventBus { + // map + private subscribers = new Map(); - subscribe(topic: string, subscriber: Subscriber): void { + subscribe(topic: string, subscriber: Subsriber): void { const list = this.subscribers.get(topic) ?? []; if (list.length === 0) { this.subscribers.set(topic, list); @@ -13,7 +13,7 @@ export class EventBus { list.push(subscriber); } - unsubscribe(topic: string, subscriber: Subscriber) { + unsubscribe(topic: string, subscriber: Subsriber) { const list = this.subscribers.get(topic); if (list) { for (let i = 0; i < list.length; i++) { diff --git a/src/event-bus/__tests__/EventBus.test.ts b/src/event-bus/__tests__/EventBus.test.ts index 80e8d186..070f43c9 100644 --- a/src/event-bus/__tests__/EventBus.test.ts +++ b/src/event-bus/__tests__/EventBus.test.ts @@ -1,4 +1,4 @@ -import { EventBus } from '../EventBus'; +import EventBus from '../EventBus'; describe('EventBus tests', () => { let eventBus: EventBus; diff --git a/src/event-cache/EventCache.ts b/src/event-cache/EventCache.ts index 7a071e19..1c6d7ac5 100644 --- a/src/event-cache/EventCache.ts +++ b/src/event-cache/EventCache.ts @@ -8,6 +8,7 @@ import { UserDetails, RumEvent } from '../dispatch/dataplane'; +import EventBus from '../event-bus/EventBus'; const webClientVersion = '1.14.0'; @@ -37,7 +38,11 @@ export class EventCache { * @param sessionManager The sessionManager returns user id, session id and handles session timeout. * @param pageManager The pageManager returns page id. */ - constructor(applicationDetails: AppMonitorDetails, config: Config) { + constructor( + applicationDetails: AppMonitorDetails, + config: Config, + private bus = new EventBus() + ) { this.appMonitorDetails = applicationDetails; this.config = config; this.enabled = true; @@ -220,13 +225,15 @@ export class EventCache { 'aws:clientVersion': webClientVersion }; - this.events.push({ + const event: RumEvent = { details: JSON.stringify(eventData), id: v4(), metadata: JSON.stringify(metaData), timestamp: new Date(), type - }); + }; + this.events.push(event); + this.bus.notify(type, event); }; /** diff --git a/src/event-cache/__tests__/EventCache.test.ts b/src/event-cache/__tests__/EventCache.test.ts index 9b009523..deb1fd99 100644 --- a/src/event-cache/__tests__/EventCache.test.ts +++ b/src/event-cache/__tests__/EventCache.test.ts @@ -5,6 +5,8 @@ import { SessionManager } from '../../sessions/SessionManager'; import { RumEvent } from '../../dispatch/dataplane'; import { DEFAULT_CONFIG, mockFetch } from '../../test-utils/test-utils'; import { INSTALL_MODULE, INSTALL_SCRIPT } from '../../utils/constants'; +import EventBus from '../../event-bus/EventBus'; +jest.mock('../../event-bus/EventBus'); global.fetch = mockFetch; const getSession = jest.fn(() => ({ @@ -492,6 +494,53 @@ describe('EventCache tests', () => { expect(eventCache.getEventBatch().length).toEqual(1); }); + test('when event is recorded then subscribers are notified', async () => { + // Init + const EVENT1_SCHEMA = 'com.amazon.rum.event1'; + const bus = new EventBus(); + const eventCache: EventCache = Utils.createEventCache( + DEFAULT_CONFIG, + bus + ); + const event = { + id: expect.stringMatching(/[0-9a-f\-]+/), + timestamp: new Date(), + type: EVENT1_SCHEMA, + metadata: `{"version":"1.0.0","aws:client":"${INSTALL_MODULE}","aws:clientVersion":"${WEB_CLIENT_VERSION}"}`, + details: '{}' + }; + + // Run + eventCache.recordEvent(EVENT1_SCHEMA, {}); + const eventBatch: RumEvent[] = eventCache.getEventBatch(); + expect(eventBatch).toEqual(expect.arrayContaining([event])); + expect(bus.notify).toHaveBeenCalledWith(EVENT1_SCHEMA, event); + }); + + test('when cache is disabled then subscribers are not notified', async () => { + // Init + const EVENT1_SCHEMA = 'com.amazon.rum.event1'; + const bus = new EventBus(); + const eventCache: EventCache = Utils.createEventCache( + DEFAULT_CONFIG, + bus + ); + const event = { + id: expect.stringMatching(/[0-9a-f\-]+/), + timestamp: new Date(), + type: EVENT1_SCHEMA, + metadata: `{"version":"1.0.0","aws:client":"${INSTALL_MODULE}","aws:clientVersion":"${WEB_CLIENT_VERSION}"}`, + details: '{}' + }; + + // Run + eventCache.disable(); + eventCache.recordEvent(EVENT1_SCHEMA, {}); + const eventBatch: RumEvent[] = eventCache.getEventBatch(); + expect(eventBatch).toHaveLength(0); + expect(bus.notify).not.toHaveBeenCalled(); + }); + test('when event limit is zero then recordEvent records all events', async () => { // Init const eventCount = 0; diff --git a/src/orchestration/Orchestration.ts b/src/orchestration/Orchestration.ts index 528480a3..9df001d7 100644 --- a/src/orchestration/Orchestration.ts +++ b/src/orchestration/Orchestration.ts @@ -24,6 +24,7 @@ import { FetchPlugin } from '../plugins/event-plugins/FetchPlugin'; import { PageViewPlugin } from '../plugins/event-plugins/PageViewPlugin'; import { PageAttributes } from '../sessions/PageManager'; import { INSTALL_MODULE } from '../utils/constants'; +import EventBus from '../event-bus/EventBus'; const DEFAULT_REGION = 'us-west-2'; const DEFAULT_ENDPOINT = `https://dataplane.rum.${DEFAULT_REGION}.amazonaws.com`; @@ -206,6 +207,7 @@ export class Orchestration { private eventCache: EventCache; private dispatchManager: Dispatch; private config: Config; + private bus = new EventBus(); /** * Instantiate the CloudWatch RUM web client and begin monitoring the @@ -444,7 +446,8 @@ export class Orchestration { config: this.config, record: this.eventCache.recordEvent, recordPageView: this.eventCache.recordPageView, - getSession: this.eventCache.getSession + getSession: this.eventCache.getSession, + bus: this.bus }; // Initialize PluginManager diff --git a/src/plugins/event-plugins/__tests__/FetchPlugin.test.ts b/src/plugins/event-plugins/__tests__/FetchPlugin.test.ts index 4d72ed8f..5de4cda8 100644 --- a/src/plugins/event-plugins/__tests__/FetchPlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/FetchPlugin.test.ts @@ -5,6 +5,7 @@ import { } from '../../utils/http-utils'; import { advanceTo } from 'jest-date-mock'; import { + context, DEFAULT_CONFIG, record, recordPageView, @@ -73,6 +74,7 @@ describe('FetchPlugin tests', () => { mockFetchWithErrorObject.mockClear(); mockFetchWithErrorObjectAndStack.mockClear(); record.mockClear(); + getSession.mockClear(); }); test('when fetch is called then the plugin records the http request/response', async () => { @@ -537,17 +539,9 @@ describe('FetchPlugin tests', () => { logicalServiceName: 'sample.rum.aws.amazon.com', urlsToInclude: [/aws\.amazon\.com/] }; - const xRayOnContext: PluginContext = { - applicationId: 'b', - applicationVersion: '1.0', - config: { ...DEFAULT_CONFIG, ...{ enableXRay: true } }, - record, - recordPageView, - getSession - }; - + const context = Object.assign({}, xRayOnContext, { getSession }); const plugin: FetchPlugin = new FetchPlugin(config); - plugin.load(xRayOnContext); + plugin.load(context); // Run await fetch(URL); @@ -566,17 +560,11 @@ describe('FetchPlugin tests', () => { logicalServiceName: 'sample.rum.aws.amazon.com', urlsToInclude: [/aws\.amazon\.com/] }; - const xRayOnContext: PluginContext = { - applicationId: 'b', - applicationVersion: '1.0', - config: { ...DEFAULT_CONFIG, ...{ enableXRay: true } }, - record, - recordPageView, - getSession - }; + const context = Object.assign({}, xRayOnContext, {getSession}) + const plugin: FetchPlugin = new FetchPlugin(config); - plugin.load(xRayOnContext); + plugin.load(context); // Run await fetch(URL); diff --git a/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts b/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts index 6bad5e8a..26fd8a91 100644 --- a/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts @@ -70,18 +70,14 @@ describe('ResourcePlugin tests', () => { test('when recordResourceUrl is false then the resource name is not recorded', async () => { // Setup mockRandom(0); // Retain order in shuffle - const context: PluginContext = { - applicationId: 'b', - applicationVersion: '1.0', - config: { ...DEFAULT_CONFIG, recordResourceUrl: false }, - record, - recordPageView, - getSession - }; + const plugin: ResourcePlugin = buildResourcePlugin(); + const mockContext = Object.assign({}, context, { + config: { ...DEFAULT_CONFIG, recordResourceUrl: false } + }); // Run - plugin.load(context); + plugin.load(mockContext); window.dispatchEvent(new Event('load')); plugin.disable(); diff --git a/src/plugins/event-plugins/__tests__/XhrPlugin.test.ts b/src/plugins/event-plugins/__tests__/XhrPlugin.test.ts index 1233715a..0fd49b50 100644 --- a/src/plugins/event-plugins/__tests__/XhrPlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/XhrPlugin.test.ts @@ -2,6 +2,7 @@ import { PartialHttpPluginConfig } from '../../utils/http-utils'; import { advanceTo } from 'jest-date-mock'; import { XhrPlugin } from '../XhrPlugin'; import { + context as mockContext, xRayOffContext, xRayOnContext, record, @@ -537,14 +538,9 @@ describe('XhrPlugin tests', () => { record: false, eventCount: 0 })); - const context: PluginContext = { - applicationId: 'b', - applicationVersion: '1.0', - config: DEFAULT_CONFIG, - record, - recordPageView, - getSession - }; + + const context = { ...mockContext, getSession }; + const config: PartialHttpPluginConfig = { logicalServiceName: 'sample.rum.aws.amazon.com', urlsToInclude: [/response\.json/] @@ -574,14 +570,7 @@ describe('XhrPlugin tests', () => { test('when getSession returns undefined then the plugin does not record a trace', async () => { // Init const getSession: jest.MockedFunction = jest.fn(); - const context: PluginContext = { - applicationId: 'b', - applicationVersion: '1.0', - config: { ...DEFAULT_CONFIG, ...{ enableXRay: true } }, - record, - recordPageView, - getSession - }; + const context = { ...mockContext, getSession }; const config: PartialHttpPluginConfig = { logicalServiceName: 'sample.rum.aws.amazon.com', urlsToInclude: [/response\.json/], diff --git a/src/plugins/types.ts b/src/plugins/types.ts index 2aec3745..aa3c7d9b 100644 --- a/src/plugins/types.ts +++ b/src/plugins/types.ts @@ -1,9 +1,9 @@ import { Config } from '../orchestration/Orchestration'; import { Session } from '../sessions/SessionManager'; +import EventBus from '../event-bus/EventBus'; export type RecordEvent = (type: string, eventData: object) => void; export type RecordPageView = (pageId: string) => void; - export type GetSession = () => Session | undefined; export type PluginContext = { @@ -13,4 +13,5 @@ export type PluginContext = { record: RecordEvent; recordPageView: RecordPageView; getSession: GetSession; + bus: EventBus; }; diff --git a/src/test-utils/test-utils.ts b/src/test-utils/test-utils.ts index 7ba16b3e..cb9b2609 100644 --- a/src/test-utils/test-utils.ts +++ b/src/test-utils/test-utils.ts @@ -17,6 +17,8 @@ import { UserDetails } from '../dispatch/dataplane'; import { ReadableStream } from 'web-streams-polyfill'; +import EventBus from '../event-bus/EventBus'; +jest.mock('../event-bus/EventBus'); export const AWS_RUM_ENDPOINT = new URL( 'https://rumservicelambda.us-west-2.amazonaws.com' @@ -64,8 +66,8 @@ export const createDefaultEventCache = (): EventCache => { return new EventCache(APP_MONITOR_DETAILS, DEFAULT_CONFIG); }; -export const createEventCache = (config: Config): EventCache => { - return new EventCache(APP_MONITOR_DETAILS, config); +export const createEventCache = (config: Config, bus?: EventBus): EventCache => { + return new EventCache(APP_MONITOR_DETAILS, config, bus); }; export const createDefaultEventCacheWithEvents = (): EventCache => { @@ -112,25 +114,18 @@ export const context: PluginContext = { config: DEFAULT_CONFIG, record, recordPageView, - getSession + getSession, + bus: new EventBus() }; export const xRayOffContext: PluginContext = { - applicationId: 'b', - applicationVersion: '1.0', - config: { ...DEFAULT_CONFIG, ...{ enableXRay: false } }, - record, - recordPageView, - getSession + ...context, + config: { ...DEFAULT_CONFIG, ...{ enableXRay: false } } }; export const xRayOnContext: PluginContext = { - applicationId: 'b', - applicationVersion: '1.0', - config: { ...DEFAULT_CONFIG, ...{ enableXRay: true } }, - record, - recordPageView, - getSession + ...context, + config: { ...DEFAULT_CONFIG, ...{ enableXRay: true } } }; export const stringToUtf16 = (inputString: string) => { From 59ad82749756eac9fbb2c5e6a984121d0ebb5f75 Mon Sep 17 00:00:00 2001 From: Billy Date: Tue, 5 Sep 2023 14:00:35 -0700 Subject: [PATCH 03/28] fix linting errors --- src/event-bus/EventBus.ts | 4 ++-- src/event-cache/__tests__/EventCache.test.ts | 4 ++-- src/plugins/event-plugins/__tests__/FetchPlugin.test.ts | 3 +-- src/test-utils/test-utils.ts | 5 ++++- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/event-bus/EventBus.ts b/src/event-bus/EventBus.ts index a63fac03..464b76ae 100644 --- a/src/event-bus/EventBus.ts +++ b/src/event-bus/EventBus.ts @@ -2,8 +2,8 @@ export type Subsriber = (payload: any) => void; /** A topic-based event bus to facilitate communication between plugins */ export default class EventBus { - // map - private subscribers = new Map(); + // map + private subscribrers = new Map(); subscribe(topic: string, subscriber: Subsriber): void { const list = this.subscribers.get(topic) ?? []; diff --git a/src/event-cache/__tests__/EventCache.test.ts b/src/event-cache/__tests__/EventCache.test.ts index deb1fd99..e14d2af4 100644 --- a/src/event-cache/__tests__/EventCache.test.ts +++ b/src/event-cache/__tests__/EventCache.test.ts @@ -514,7 +514,7 @@ describe('EventCache tests', () => { eventCache.recordEvent(EVENT1_SCHEMA, {}); const eventBatch: RumEvent[] = eventCache.getEventBatch(); expect(eventBatch).toEqual(expect.arrayContaining([event])); - expect(bus.notify).toHaveBeenCalledWith(EVENT1_SCHEMA, event); + expect(bus.notify).toHaveBeenCalledWith(EVENT1_SCHEMA, event); // eslint-disable-line }); test('when cache is disabled then subscribers are not notified', async () => { @@ -538,7 +538,7 @@ describe('EventCache tests', () => { eventCache.recordEvent(EVENT1_SCHEMA, {}); const eventBatch: RumEvent[] = eventCache.getEventBatch(); expect(eventBatch).toHaveLength(0); - expect(bus.notify).not.toHaveBeenCalled(); + expect(bus.notify).not.toHaveBeenCalled(); // eslint-disable-line }); test('when event limit is zero then recordEvent records all events', async () => { diff --git a/src/plugins/event-plugins/__tests__/FetchPlugin.test.ts b/src/plugins/event-plugins/__tests__/FetchPlugin.test.ts index 5de4cda8..b30c688e 100644 --- a/src/plugins/event-plugins/__tests__/FetchPlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/FetchPlugin.test.ts @@ -560,8 +560,7 @@ describe('FetchPlugin tests', () => { logicalServiceName: 'sample.rum.aws.amazon.com', urlsToInclude: [/aws\.amazon\.com/] }; - const context = Object.assign({}, xRayOnContext, {getSession}) - + const context = Object.assign({}, xRayOnContext, { getSession }); const plugin: FetchPlugin = new FetchPlugin(config); plugin.load(context); diff --git a/src/test-utils/test-utils.ts b/src/test-utils/test-utils.ts index cb9b2609..2bf95e53 100644 --- a/src/test-utils/test-utils.ts +++ b/src/test-utils/test-utils.ts @@ -66,7 +66,10 @@ export const createDefaultEventCache = (): EventCache => { return new EventCache(APP_MONITOR_DETAILS, DEFAULT_CONFIG); }; -export const createEventCache = (config: Config, bus?: EventBus): EventCache => { +export const createEventCache = ( + config: Config, + bus?: EventBus +): EventCache => { return new EventCache(APP_MONITOR_DETAILS, config, bus); }; From 8975c04008cd0cad72d408d86d3d1df224f049ae Mon Sep 17 00:00:00 2001 From: Billy Date: Tue, 5 Sep 2023 15:04:05 -0700 Subject: [PATCH 04/28] fix rebase error --- src/event-bus/EventBus.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/event-bus/EventBus.ts b/src/event-bus/EventBus.ts index 464b76ae..2307ec51 100644 --- a/src/event-bus/EventBus.ts +++ b/src/event-bus/EventBus.ts @@ -1,11 +1,11 @@ -export type Subsriber = (payload: any) => void; +export type Subscriber = (payload: any) => void; /** A topic-based event bus to facilitate communication between plugins */ export default class EventBus { // map - private subscribrers = new Map(); + private subscribers = new Map(); - subscribe(topic: string, subscriber: Subsriber): void { + subscribe(topic: string, subscriber: Subscriber): void { const list = this.subscribers.get(topic) ?? []; if (list.length === 0) { this.subscribers.set(topic, list); @@ -13,7 +13,7 @@ export default class EventBus { list.push(subscriber); } - unsubscribe(topic: string, subscriber: Subsriber) { + unsubscribe(topic: string, subscriber: Subscriber) { const list = this.subscribers.get(topic); if (list) { for (let i = 0; i < list.length; i++) { From a3c7c786a9cb0fb9bba9b509c578af0f520620e9 Mon Sep 17 00:00:00 2001 From: Billy Date: Tue, 5 Sep 2023 15:36:20 -0700 Subject: [PATCH 05/28] refactor: EventCache publishes raw RumEvent --- src/event-cache/EventCache.ts | 16 ++++++++---- src/event-cache/__tests__/EventCache.test.ts | 27 ++++++++++++-------- 2 files changed, 28 insertions(+), 15 deletions(-) diff --git a/src/event-cache/EventCache.ts b/src/event-cache/EventCache.ts index 1c6d7ac5..bc404f6b 100644 --- a/src/event-cache/EventCache.ts +++ b/src/event-cache/EventCache.ts @@ -225,15 +225,21 @@ export class EventCache { 'aws:clientVersion': webClientVersion }; - const event: RumEvent = { - details: JSON.stringify(eventData), + const partialEvent = { id: v4(), - metadata: JSON.stringify(metaData), timestamp: new Date(), type }; - this.events.push(event); - this.bus.notify(type, event); + this.bus.notify(type, { + ...partialEvent, + details: eventData, + metadata: metaData + }); + this.events.push({ + ...partialEvent, + details: JSON.stringify(eventData), + metadata: JSON.stringify(metaData) + }); }; /** diff --git a/src/event-cache/__tests__/EventCache.test.ts b/src/event-cache/__tests__/EventCache.test.ts index e14d2af4..481d506b 100644 --- a/src/event-cache/__tests__/EventCache.test.ts +++ b/src/event-cache/__tests__/EventCache.test.ts @@ -494,7 +494,7 @@ describe('EventCache tests', () => { expect(eventCache.getEventBatch().length).toEqual(1); }); - test('when event is recorded then subscribers are notified', async () => { + test('when event is recorded then subscribers are notified with raw event', async () => { // Init const EVENT1_SCHEMA = 'com.amazon.rum.event1'; const bus = new EventBus(); @@ -502,6 +502,7 @@ describe('EventCache tests', () => { DEFAULT_CONFIG, bus ); + const event = { id: expect.stringMatching(/[0-9a-f\-]+/), timestamp: new Date(), @@ -514,7 +515,21 @@ describe('EventCache tests', () => { eventCache.recordEvent(EVENT1_SCHEMA, {}); const eventBatch: RumEvent[] = eventCache.getEventBatch(); expect(eventBatch).toEqual(expect.arrayContaining([event])); - expect(bus.notify).toHaveBeenCalledWith(EVENT1_SCHEMA, event); // eslint-disable-line + // eslint-disable-next-line + expect(bus.notify).toHaveBeenCalledWith( + EVENT1_SCHEMA, + expect.objectContaining({ + id: expect.stringMatching(/[0-9a-f\-]+/), + timestamp: new Date(), + type: EVENT1_SCHEMA, + metadata: expect.objectContaining({ + version: '1.0.0', + 'aws:client': INSTALL_MODULE, + 'aws:clientVersion': WEB_CLIENT_VERSION + }), + details: expect.objectContaining({}) + }) + ); }); test('when cache is disabled then subscribers are not notified', async () => { @@ -525,14 +540,6 @@ describe('EventCache tests', () => { DEFAULT_CONFIG, bus ); - const event = { - id: expect.stringMatching(/[0-9a-f\-]+/), - timestamp: new Date(), - type: EVENT1_SCHEMA, - metadata: `{"version":"1.0.0","aws:client":"${INSTALL_MODULE}","aws:clientVersion":"${WEB_CLIENT_VERSION}"}`, - details: '{}' - }; - // Run eventCache.disable(); eventCache.recordEvent(EVENT1_SCHEMA, {}); From 6bb67e8345d3ffce1aeb50d733b6b6cd0936808d Mon Sep 17 00:00:00 2001 From: Billy Date: Tue, 5 Sep 2023 18:02:38 -0700 Subject: [PATCH 06/28] fix: EventCache.record returns ParsedRumEvent without publishing --- src/dispatch/dataplane.ts | 10 ++++ src/event-cache/EventCache.ts | 25 +++++---- src/event-cache/__tests__/EventCache.test.ts | 56 ++++++-------------- src/plugins/types.ts | 6 ++- 4 files changed, 46 insertions(+), 51 deletions(-) diff --git a/src/dispatch/dataplane.ts b/src/dispatch/dataplane.ts index 06a8d1ed..742413b9 100644 --- a/src/dispatch/dataplane.ts +++ b/src/dispatch/dataplane.ts @@ -5,6 +5,8 @@ // use the type definitions from the CloudWatch RUM SDK, we have made a copy of // them here to completely remove the dependency on the CloudWatch RUM SDK. +import { MetaData } from '../events/meta-data'; + export interface PutRumEventsRequest { BatchId: string; AppMonitorDetails: AppMonitorDetails; @@ -29,3 +31,11 @@ export interface RumEvent { metadata?: string; details: string; } + +export interface ParsedRumEvent { + id: string; + timestamp: Date; + type: string; + metadata?: MetaData; + details: object; +} diff --git a/src/event-cache/EventCache.ts b/src/event-cache/EventCache.ts index bc404f6b..85c9ebf0 100644 --- a/src/event-cache/EventCache.ts +++ b/src/event-cache/EventCache.ts @@ -6,7 +6,8 @@ import { PageAttributes, PageManager } from '../sessions/PageManager'; import { AppMonitorDetails, UserDetails, - RumEvent + RumEvent, + ParsedRumEvent } from '../dispatch/dataplane'; import EventBus from '../event-bus/EventBus'; @@ -98,7 +99,7 @@ export class EventCache { this.sessionManager.incrementSessionEventCount(); if (this.canRecord(session)) { - this.addRecordToCache(type, eventData); + return this.addRecordToCache(type, eventData); } } }; @@ -203,7 +204,10 @@ export class EventCache { * * @param type The event schema. */ - private addRecordToCache = (type: string, eventData: object) => { + private addRecordToCache = ( + type: string, + eventData: object + ): ParsedRumEvent => { if (!this.enabled) { return; } @@ -225,21 +229,22 @@ export class EventCache { 'aws:clientVersion': webClientVersion }; - const partialEvent = { + const partial = { id: v4(), timestamp: new Date(), type }; - this.bus.notify(type, { - ...partialEvent, - details: eventData, - metadata: metaData - }); + this.events.push({ - ...partialEvent, + ...partial, details: JSON.stringify(eventData), metadata: JSON.stringify(metaData) }); + return { + ...partial, + details: eventData, + metadata: metaData + }; }; /** diff --git a/src/event-cache/__tests__/EventCache.test.ts b/src/event-cache/__tests__/EventCache.test.ts index 481d506b..39217b55 100644 --- a/src/event-cache/__tests__/EventCache.test.ts +++ b/src/event-cache/__tests__/EventCache.test.ts @@ -5,8 +5,6 @@ import { SessionManager } from '../../sessions/SessionManager'; import { RumEvent } from '../../dispatch/dataplane'; import { DEFAULT_CONFIG, mockFetch } from '../../test-utils/test-utils'; import { INSTALL_MODULE, INSTALL_SCRIPT } from '../../utils/constants'; -import EventBus from '../../event-bus/EventBus'; -jest.mock('../../event-bus/EventBus'); global.fetch = mockFetch; const getSession = jest.fn(() => ({ @@ -494,14 +492,10 @@ describe('EventCache tests', () => { expect(eventCache.getEventBatch().length).toEqual(1); }); - test('when event is recorded then subscribers are notified with raw event', async () => { + test('when event is recorded then the parsed event is returned', async () => { // Init const EVENT1_SCHEMA = 'com.amazon.rum.event1'; - const bus = new EventBus(); - const eventCache: EventCache = Utils.createEventCache( - DEFAULT_CONFIG, - bus - ); + const eventCache: EventCache = Utils.createDefaultEventCache(); const event = { id: expect.stringMatching(/[0-9a-f\-]+/), @@ -511,41 +505,23 @@ describe('EventCache tests', () => { details: '{}' }; - // Run - eventCache.recordEvent(EVENT1_SCHEMA, {}); - const eventBatch: RumEvent[] = eventCache.getEventBatch(); - expect(eventBatch).toEqual(expect.arrayContaining([event])); - // eslint-disable-next-line - expect(bus.notify).toHaveBeenCalledWith( - EVENT1_SCHEMA, - expect.objectContaining({ - id: expect.stringMatching(/[0-9a-f\-]+/), - timestamp: new Date(), - type: EVENT1_SCHEMA, - metadata: expect.objectContaining({ - version: '1.0.0', - 'aws:client': INSTALL_MODULE, - 'aws:clientVersion': WEB_CLIENT_VERSION - }), - details: expect.objectContaining({}) - }) - ); - }); + const parsedEventMatcher = { + id: expect.stringMatching(/[0-9a-f\-]+/), + timestamp: new Date(), + type: EVENT1_SCHEMA, + metadata: expect.objectContaining({ + version: '1.0.0', + 'aws:client': INSTALL_MODULE, + 'aws:clientVersion': WEB_CLIENT_VERSION + }), + details: expect.objectContaining({}) + }; - test('when cache is disabled then subscribers are not notified', async () => { - // Init - const EVENT1_SCHEMA = 'com.amazon.rum.event1'; - const bus = new EventBus(); - const eventCache: EventCache = Utils.createEventCache( - DEFAULT_CONFIG, - bus - ); // Run - eventCache.disable(); - eventCache.recordEvent(EVENT1_SCHEMA, {}); + const parsedEvent = eventCache.recordEvent(EVENT1_SCHEMA, {}); const eventBatch: RumEvent[] = eventCache.getEventBatch(); - expect(eventBatch).toHaveLength(0); - expect(bus.notify).not.toHaveBeenCalled(); // eslint-disable-line + expect(eventBatch).toEqual(expect.arrayContaining([event])); + expect(parsedEvent).toEqual(parsedEventMatcher); }); test('when event limit is zero then recordEvent records all events', async () => { diff --git a/src/plugins/types.ts b/src/plugins/types.ts index aa3c7d9b..445f56b2 100644 --- a/src/plugins/types.ts +++ b/src/plugins/types.ts @@ -1,8 +1,12 @@ import { Config } from '../orchestration/Orchestration'; import { Session } from '../sessions/SessionManager'; import EventBus from '../event-bus/EventBus'; +import { ParsedRumEvent } from 'dispatch/dataplane'; -export type RecordEvent = (type: string, eventData: object) => void; +export type RecordEvent = ( + type: string, + eventData: object +) => ParsedRumEvent | void; export type RecordPageView = (pageId: string) => void; export type GetSession = () => Session | undefined; From 95233209436433b53b97cedceb968e8173d5c034 Mon Sep 17 00:00:00 2001 From: Billy Date: Tue, 5 Sep 2023 18:47:27 -0700 Subject: [PATCH 07/28] chore: publish resource events --- src/event-cache/EventCache.ts | 1 + src/event-cache/__tests__/EventCache.test.ts | 6 +++- src/plugins/event-plugins/ResourcePlugin.ts | 16 ++++++++-- .../__tests__/ResourcePlugin.test.ts | 30 +++++++++++++++++-- src/test-utils/mock-data.ts | 16 ++++++++++ 5 files changed, 64 insertions(+), 5 deletions(-) diff --git a/src/event-cache/EventCache.ts b/src/event-cache/EventCache.ts index 85c9ebf0..0490ee23 100644 --- a/src/event-cache/EventCache.ts +++ b/src/event-cache/EventCache.ts @@ -88,6 +88,7 @@ export class EventCache { * If the session is not being recorded, the event will not be recorded. * * @param type The event schema. + * @returns {ParsedRumEvent} if the event was recorded */ public recordEvent = (type: string, eventData: object) => { if (!this.enabled) { diff --git a/src/event-cache/__tests__/EventCache.test.ts b/src/event-cache/__tests__/EventCache.test.ts index 39217b55..66fd3610 100644 --- a/src/event-cache/__tests__/EventCache.test.ts +++ b/src/event-cache/__tests__/EventCache.test.ts @@ -3,7 +3,11 @@ import { advanceTo } from 'jest-date-mock'; import * as Utils from '../../test-utils/test-utils'; import { SessionManager } from '../../sessions/SessionManager'; import { RumEvent } from '../../dispatch/dataplane'; -import { DEFAULT_CONFIG, mockFetch } from '../../test-utils/test-utils'; +import { + DEFAULT_CONFIG, + WEB_CLIENT_VERSION, + mockFetch +} from '../../test-utils/test-utils'; import { INSTALL_MODULE, INSTALL_SCRIPT } from '../../utils/constants'; global.fetch = mockFetch; diff --git a/src/plugins/event-plugins/ResourcePlugin.ts b/src/plugins/event-plugins/ResourcePlugin.ts index 6d0c35ac..301b69e4 100644 --- a/src/plugins/event-plugins/ResourcePlugin.ts +++ b/src/plugins/event-plugins/ResourcePlugin.ts @@ -11,6 +11,7 @@ import { PartialPerformancePluginConfig, PerformancePluginConfig } from '../utils/performance-utils'; +import { ParsedRumEvent } from 'dispatch/dataplane'; export const RESOURCE_EVENT_PLUGIN_ID = 'resource'; @@ -117,17 +118,28 @@ export class ResourcePlugin extends InternalPlugin { } if (this.context?.record) { + const fileType = getResourceFileType(entryData.name); const eventData: ResourceEvent = { version: '1.0.0', initiatorType: entryData.initiatorType, duration: entryData.duration, - fileType: getResourceFileType(entryData.name), + fileType, transferSize: entryData.transferSize }; if (this.context.config.recordResourceUrl) { eventData.targetUrl = entryData.name; } - this.context.record(PERFORMANCE_RESOURCE_EVENT_TYPE, eventData); + const parsedEvent = this.context.record( + PERFORMANCE_RESOURCE_EVENT_TYPE, + eventData + ); + + if (parsedEvent) { + this.context?.bus.notify(PERFORMANCE_RESOURCE_EVENT_TYPE, [ + entryData, + parsedEvent + ]); + } } }; diff --git a/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts b/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts index 26fd8a91..fb13c98c 100644 --- a/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts @@ -9,7 +9,8 @@ import { mockPerformanceObjectWith, putRumEventsDocument, putRumEventsGammaDocument, - dataPlaneDocument + dataPlaneDocument, + parsedRumEvent } from '../../../test-utils/mock-data'; import { ResourcePlugin } from '../ResourcePlugin'; import { mockRandom } from 'jest-mock-random'; @@ -22,7 +23,7 @@ import { } from '../../../test-utils/test-utils'; import { PERFORMANCE_RESOURCE_EVENT_TYPE } from '../../utils/constant'; import { ResourceEvent } from '../../../events/resource-event'; -import { PluginContext } from '../../types'; +import { PluginContext, RecordEvent } from '../../types'; import { PartialPerformancePluginConfig } from 'plugins/utils/performance-utils'; const buildResourcePlugin = (config?: PartialPerformancePluginConfig) => { @@ -263,4 +264,29 @@ describe('ResourcePlugin tests', () => { expect(record).not.toHaveBeenCalled(); }); + + test('when entry is recorded then it is published to event bus', async () => { + // Setup + mockRandom(0); // Retain order in shuffle + + const plugin: ResourcePlugin = buildResourcePlugin(); + const record: jest.MockedFunction = jest + .fn() + .mockReturnValue({ ...parsedRumEvent }); + const mockContext = { + ...context, + record + }; + + // Run + plugin.load(mockContext); + window.dispatchEvent(new Event('load')); + plugin.disable(); + + // Assert + expect(record.mock.calls[0][0]).toEqual( + PERFORMANCE_RESOURCE_EVENT_TYPE + ); + expect(context.bus.notify).toHaveBeenCalled(); // eslint-disable-line + }); }); diff --git a/src/test-utils/mock-data.ts b/src/test-utils/mock-data.ts index bf1b2348..79299f8d 100644 --- a/src/test-utils/mock-data.ts +++ b/src/test-utils/mock-data.ts @@ -1,3 +1,7 @@ +/* eslint max-classes-per-file: 0 */ +import { INSTALL_MODULE } from '../utils/constants'; +import { WEB_CLIENT_VERSION } from './test-utils'; + /* eslint-disable max-classes-per-file */ export const firstPaintEvent = { name: 'first-paint', @@ -458,3 +462,15 @@ export const httpErrorEvent = { 'https://jtrm21au2a.execute-api.us-west-2.amazonaws.com/alpha/v1.0.0/putBatchMetrics', responseText: '{"message":"Could not persist data"}' }; + +export const parsedRumEvent = { + id: expect.stringMatching(/[0-9a-f\-]+/), + timestamp: new Date(), + type: 'com.amazon.rum.event', + metadata: { + version: '1.0.0', + 'aws:client': INSTALL_MODULE, + 'aws:clientVersion': WEB_CLIENT_VERSION + }, + details: {} +}; From 751fc6ebfad56778491ac27d2172acf04c81f145 Mon Sep 17 00:00:00 2001 From: Billy Date: Tue, 5 Sep 2023 19:14:16 -0700 Subject: [PATCH 08/28] chore: publish navigation events --- src/plugins/event-plugins/NavigationPlugin.ts | 28 +++++++++++-------- .../__tests__/NavigationPlugin.test.ts | 25 ++++++++++++++--- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/src/plugins/event-plugins/NavigationPlugin.ts b/src/plugins/event-plugins/NavigationPlugin.ts index 788ebe92..b535ee51 100644 --- a/src/plugins/event-plugins/NavigationPlugin.ts +++ b/src/plugins/event-plugins/NavigationPlugin.ts @@ -178,12 +178,7 @@ export class NavigationPlugin extends InternalPlugin { duration: entryData.loadEventEnd - entryData.navigationStart, navigationTimingLevel: 1 }; - if (this.context?.record) { - this.context.record( - PERFORMANCE_NAVIGATION_EVENT_TYPE, - eventDataNavigationTimingLevel1 - ); - } + this.recordEvent(eventDataNavigationTimingLevel1); }; // Timeout is required for loadEventEnd to complete setTimeout(recordNavigation, 0); @@ -262,12 +257,7 @@ export class NavigationPlugin extends InternalPlugin { navigationTimingLevel: 2 }; - if (this.context?.record) { - this.context.record( - PERFORMANCE_NAVIGATION_EVENT_TYPE, - eventDataNavigationTimingLevel2 - ); - } + this.recordEvent(eventDataNavigationTimingLevel2); }; /** @@ -292,4 +282,18 @@ export class NavigationPlugin extends InternalPlugin { } } } + + /** Record event and publish if record was successful */ + private recordEvent(event: NavigationEvent) { + const rawEvent = this.context?.record( + PERFORMANCE_NAVIGATION_EVENT_TYPE, + event + ); + if (rawEvent) { + this.context?.bus.notify( + PERFORMANCE_NAVIGATION_EVENT_TYPE, + rawEvent + ); + } + } } diff --git a/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts b/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts index 6e40578e..50629372 100644 --- a/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts @@ -5,12 +5,14 @@ import { mockPerformanceObserver, MockPerformanceTiming, mockPerformanceObjectWith, - putRumEventsDocument + putRumEventsDocument, + parsedRumEvent } from '../../../test-utils/mock-data'; import { NavigationPlugin } from '../NavigationPlugin'; import { context, record } from '../../../test-utils/test-utils'; import { PERFORMANCE_NAVIGATION_EVENT_TYPE } from '../../utils/constant'; import { PartialPerformancePluginConfig } from 'plugins/utils/performance-utils'; +import { RecordEvent } from 'plugins/types'; const buildNavigationPlugin = (config?: PartialPerformancePluginConfig) => { return new NavigationPlugin(config); @@ -30,7 +32,14 @@ describe('NavigationPlugin tests', () => { test('When navigation event is present then event is recorded', async () => { const plugin: NavigationPlugin = buildNavigationPlugin(); // Run - plugin.load(context); + const record: jest.MockedFunction = jest + .fn() + .mockReturnValue({ ...parsedRumEvent }); + const mockContext = { + ...context, + record + }; + plugin.load(mockContext); window.dispatchEvent(new Event('load')); plugin.disable(); @@ -44,6 +53,7 @@ describe('NavigationPlugin tests', () => { navigationType: navigationEvent.type }) ); + expect(context.bus.notify).toHaveBeenCalledTimes(1); // eslint-disable-line }); test('When navigation timing level 2 API is not present then navigation timing level 1 API is recorded', async () => { @@ -52,8 +62,14 @@ describe('NavigationPlugin tests', () => { mockPerformanceObserver(); const plugin: NavigationPlugin = buildNavigationPlugin(); - - plugin.load(context); + const record: jest.MockedFunction = jest + .fn() + .mockReturnValue({ ...parsedRumEvent }); + const mockContext = { + ...context, + record + }; + plugin.load(mockContext); window.dispatchEvent(new Event('load')); plugin.disable(); @@ -77,6 +93,7 @@ describe('NavigationPlugin tests', () => { navigationTimingLevel: 1 }) ); + expect(context.bus.notify).toHaveBeenCalledTimes(1); // eslint-disable-line }); test('when enabled then events are recorded', async () => { From 22a604aac089c539cf8f1a1513a333b6355f9b56 Mon Sep 17 00:00:00 2001 From: Billy Date: Wed, 6 Sep 2023 08:44:31 -0700 Subject: [PATCH 09/28] chore: resolve nits --- src/event-bus/EventBus.ts | 10 +++++++--- src/event-bus/__tests__/EventBus.test.ts | 18 ++++++++++++------ src/event-cache/EventCache.ts | 9 ++++++--- src/plugins/event-plugins/DemoPlugin.ts | 2 +- src/plugins/event-plugins/NavigationPlugin.ts | 7 +++---- src/plugins/event-plugins/ResourcePlugin.ts | 9 ++++----- .../__tests__/NavigationPlugin.test.ts | 4 ++-- .../__tests__/ResourcePlugin.test.ts | 4 ++-- src/plugins/types.ts | 4 ++-- 9 files changed, 39 insertions(+), 28 deletions(-) diff --git a/src/event-bus/EventBus.ts b/src/event-bus/EventBus.ts index 2307ec51..b70de964 100644 --- a/src/event-bus/EventBus.ts +++ b/src/event-bus/EventBus.ts @@ -1,4 +1,8 @@ -export type Subscriber = (payload: any) => void; +export type Subscriber = (message: any) => void; +export interface Message { + key?: any; + payload: any; +} /** A topic-based event bus to facilitate communication between plugins */ export default class EventBus { @@ -26,11 +30,11 @@ export default class EventBus { return false; } - notify(topic: string, payload: any): void { + dispatch(topic: string, message: Message): void { const list = this.subscribers.get(topic); if (list) { for (const subscriber of list) { - subscriber(payload); + subscriber(message); } } } diff --git a/src/event-bus/__tests__/EventBus.test.ts b/src/event-bus/__tests__/EventBus.test.ts index 070f43c9..b013c929 100644 --- a/src/event-bus/__tests__/EventBus.test.ts +++ b/src/event-bus/__tests__/EventBus.test.ts @@ -8,17 +8,21 @@ describe('EventBus tests', () => { eventBus = new EventBus(); jest.clearAllMocks(); }); - test('when notify is invoked then all listeners are called', async () => { + test('when dispatch is invoked then all listeners are called', async () => { // init eventBus.subscribe('food', l1); eventBus.subscribe('food', l2); // run - eventBus.notify('food', 'burger'); + eventBus.dispatch('food', { key: 'bk', payload: 'whopper' }); // assert - expect(l1).toHaveBeenCalledWith('burger'); - expect(l2).toHaveBeenCalledWith('burger'); + expect(l1).toHaveBeenCalledWith( + expect.objectContaining({ key: 'bk', payload: 'whopper' }) + ); + expect(l2).toHaveBeenCalledWith( + expect.objectContaining({ key: 'bk', payload: 'whopper' }) + ); }); test('when listener is removed then it is not called', async () => { @@ -28,10 +32,12 @@ describe('EventBus tests', () => { const removed = eventBus.unsubscribe('food', l2); // run - eventBus.notify('food', 'burger'); + eventBus.dispatch('food', { payload: 'sushi' }); // assert - expect(l1).toHaveBeenCalledWith('burger'); + expect(l1).toHaveBeenCalledWith( + expect.objectContaining({ payload: 'sushi' }) + ); expect(removed).toBe(true); expect(l2).not.toHaveBeenCalled(); }); diff --git a/src/event-cache/EventCache.ts b/src/event-cache/EventCache.ts index 0490ee23..4084ffa3 100644 --- a/src/event-cache/EventCache.ts +++ b/src/event-cache/EventCache.ts @@ -88,9 +88,12 @@ export class EventCache { * If the session is not being recorded, the event will not be recorded. * * @param type The event schema. - * @returns {ParsedRumEvent} if the event was recorded + * @returns {ParsedRumEvent | undefined} if the event was recorded */ - public recordEvent = (type: string, eventData: object) => { + public recordEvent = ( + type: string, + eventData: object + ): ParsedRumEvent | undefined => { if (!this.enabled) { return; } @@ -208,7 +211,7 @@ export class EventCache { private addRecordToCache = ( type: string, eventData: object - ): ParsedRumEvent => { + ): ParsedRumEvent | undefined => { if (!this.enabled) { return; } diff --git a/src/plugins/event-plugins/DemoPlugin.ts b/src/plugins/event-plugins/DemoPlugin.ts index e7e72f27..6b45d178 100644 --- a/src/plugins/event-plugins/DemoPlugin.ts +++ b/src/plugins/event-plugins/DemoPlugin.ts @@ -19,7 +19,7 @@ export class DemoPlugin implements Plugin { this.configuration = {}; this.timerId = undefined; // eslint-disable-next-line @typescript-eslint/no-empty-function - this.recordEvent = () => {}; + this.recordEvent = () => undefined; } getPluginId(): string { diff --git a/src/plugins/event-plugins/NavigationPlugin.ts b/src/plugins/event-plugins/NavigationPlugin.ts index b535ee51..3bcfd7ff 100644 --- a/src/plugins/event-plugins/NavigationPlugin.ts +++ b/src/plugins/event-plugins/NavigationPlugin.ts @@ -290,10 +290,9 @@ export class NavigationPlugin extends InternalPlugin { event ); if (rawEvent) { - this.context?.bus.notify( - PERFORMANCE_NAVIGATION_EVENT_TYPE, - rawEvent - ); + this.context?.bus.dispatch(PERFORMANCE_NAVIGATION_EVENT_TYPE, { + payload: rawEvent + }); } } } diff --git a/src/plugins/event-plugins/ResourcePlugin.ts b/src/plugins/event-plugins/ResourcePlugin.ts index 301b69e4..993b2d14 100644 --- a/src/plugins/event-plugins/ResourcePlugin.ts +++ b/src/plugins/event-plugins/ResourcePlugin.ts @@ -11,7 +11,6 @@ import { PartialPerformancePluginConfig, PerformancePluginConfig } from '../utils/performance-utils'; -import { ParsedRumEvent } from 'dispatch/dataplane'; export const RESOURCE_EVENT_PLUGIN_ID = 'resource'; @@ -135,10 +134,10 @@ export class ResourcePlugin extends InternalPlugin { ); if (parsedEvent) { - this.context?.bus.notify(PERFORMANCE_RESOURCE_EVENT_TYPE, [ - entryData, - parsedEvent - ]); + this.context?.bus.dispatch(PERFORMANCE_RESOURCE_EVENT_TYPE, { + key: entryData, + payload: parsedEvent + }); } } }; diff --git a/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts b/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts index 50629372..b9b65e5f 100644 --- a/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts @@ -53,7 +53,7 @@ describe('NavigationPlugin tests', () => { navigationType: navigationEvent.type }) ); - expect(context.bus.notify).toHaveBeenCalledTimes(1); // eslint-disable-line + expect(context.bus.dispatch).toHaveBeenCalledTimes(1); // eslint-disable-line }); test('When navigation timing level 2 API is not present then navigation timing level 1 API is recorded', async () => { @@ -93,7 +93,7 @@ describe('NavigationPlugin tests', () => { navigationTimingLevel: 1 }) ); - expect(context.bus.notify).toHaveBeenCalledTimes(1); // eslint-disable-line + expect(context.bus.dispatch).toHaveBeenCalledTimes(1); // eslint-disable-line }); test('when enabled then events are recorded', async () => { diff --git a/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts b/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts index fb13c98c..0e3abfab 100644 --- a/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts @@ -265,7 +265,7 @@ describe('ResourcePlugin tests', () => { expect(record).not.toHaveBeenCalled(); }); - test('when entry is recorded then it is published to event bus', async () => { + test('when entry is recorded then it is dispatched to event bus', async () => { // Setup mockRandom(0); // Retain order in shuffle @@ -287,6 +287,6 @@ describe('ResourcePlugin tests', () => { expect(record.mock.calls[0][0]).toEqual( PERFORMANCE_RESOURCE_EVENT_TYPE ); - expect(context.bus.notify).toHaveBeenCalled(); // eslint-disable-line + expect(context.bus.dispatch).toHaveBeenCalled(); // eslint-disable-line }); }); diff --git a/src/plugins/types.ts b/src/plugins/types.ts index 445f56b2..9891a402 100644 --- a/src/plugins/types.ts +++ b/src/plugins/types.ts @@ -1,12 +1,12 @@ import { Config } from '../orchestration/Orchestration'; import { Session } from '../sessions/SessionManager'; import EventBus from '../event-bus/EventBus'; -import { ParsedRumEvent } from 'dispatch/dataplane'; +import { ParsedRumEvent } from '../dispatch/dataplane'; export type RecordEvent = ( type: string, eventData: object -) => ParsedRumEvent | void; +) => ParsedRumEvent | undefined; export type RecordPageView = (pageId: string) => void; export type GetSession = () => Session | undefined; From 15eb2f3a111bedf5e63ca4c70b970783a799eb1b Mon Sep 17 00:00:00 2001 From: Billy Date: Wed, 6 Sep 2023 09:14:11 -0700 Subject: [PATCH 10/28] chore: only publish image resource events --- src/plugins/event-plugins/ResourcePlugin.ts | 2 +- .../event-plugins/__tests__/ResourcePlugin.test.ts | 9 +++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/plugins/event-plugins/ResourcePlugin.ts b/src/plugins/event-plugins/ResourcePlugin.ts index 993b2d14..0ff4a99a 100644 --- a/src/plugins/event-plugins/ResourcePlugin.ts +++ b/src/plugins/event-plugins/ResourcePlugin.ts @@ -133,7 +133,7 @@ export class ResourcePlugin extends InternalPlugin { eventData ); - if (parsedEvent) { + if (parsedEvent && fileType === ResourceType.IMAGE) { this.context?.bus.dispatch(PERFORMANCE_RESOURCE_EVENT_TYPE, { key: entryData, payload: parsedEvent diff --git a/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts b/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts index 0e3abfab..53dd785a 100644 --- a/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts @@ -25,6 +25,7 @@ import { PERFORMANCE_RESOURCE_EVENT_TYPE } from '../../utils/constant'; import { ResourceEvent } from '../../../events/resource-event'; import { PluginContext, RecordEvent } from '../../types'; import { PartialPerformancePluginConfig } from 'plugins/utils/performance-utils'; +import { ResourceType } from '../../../utils/common-utils'; const buildResourcePlugin = (config?: PartialPerformancePluginConfig) => { return new ResourcePlugin(config); @@ -265,10 +266,11 @@ describe('ResourcePlugin tests', () => { expect(record).not.toHaveBeenCalled(); }); - test('when entry is recorded then it is dispatched to event bus', async () => { + test('when image is recorded then it is dispatched', async () => { // Setup mockRandom(0); // Retain order in shuffle - + mockPerformanceObjectWith([imageResourceEvent], [], []); + mockPerformanceObserver(); const plugin: ResourcePlugin = buildResourcePlugin(); const record: jest.MockedFunction = jest .fn() @@ -287,6 +289,9 @@ describe('ResourcePlugin tests', () => { expect(record.mock.calls[0][0]).toEqual( PERFORMANCE_RESOURCE_EVENT_TYPE ); + expect((record.mock.calls[0][1] as ResourceEvent).fileType).toEqual( + ResourceType.IMAGE + ); expect(context.bus.dispatch).toHaveBeenCalled(); // eslint-disable-line }); }); From ebe415e0db0ab1782c68881be0b6862409457d05 Mon Sep 17 00:00:00 2001 From: Billy Date: Wed, 6 Sep 2023 09:14:37 -0700 Subject: [PATCH 11/28] chore: only publish the first navigation event --- src/plugins/event-plugins/NavigationPlugin.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/plugins/event-plugins/NavigationPlugin.ts b/src/plugins/event-plugins/NavigationPlugin.ts index 3bcfd7ff..c0532047 100644 --- a/src/plugins/event-plugins/NavigationPlugin.ts +++ b/src/plugins/event-plugins/NavigationPlugin.ts @@ -283,15 +283,17 @@ export class NavigationPlugin extends InternalPlugin { } } + private dispatchedLoad = false; /** Record event and publish if record was successful */ private recordEvent(event: NavigationEvent) { - const rawEvent = this.context?.record( + const parsedEvent = this.context?.record( PERFORMANCE_NAVIGATION_EVENT_TYPE, event ); - if (rawEvent) { + if (parsedEvent && !this.dispatchedLoad) { + this.dispatchedLoad = true; this.context?.bus.dispatch(PERFORMANCE_NAVIGATION_EVENT_TYPE, { - payload: rawEvent + payload: parsedEvent }); } } From 269eb35ff36b2517e213fa992a644c2f6dc48f18 Mon Sep 17 00:00:00 2001 From: Billy Date: Fri, 8 Sep 2023 10:41:00 -0700 Subject: [PATCH 12/28] Revert "chore: only publish the first navigation event" This reverts commit 620cc4c902c68986e8407637a60b0d3bc6a4ef3f. --- src/plugins/event-plugins/NavigationPlugin.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/plugins/event-plugins/NavigationPlugin.ts b/src/plugins/event-plugins/NavigationPlugin.ts index c0532047..3bcfd7ff 100644 --- a/src/plugins/event-plugins/NavigationPlugin.ts +++ b/src/plugins/event-plugins/NavigationPlugin.ts @@ -283,17 +283,15 @@ export class NavigationPlugin extends InternalPlugin { } } - private dispatchedLoad = false; /** Record event and publish if record was successful */ private recordEvent(event: NavigationEvent) { - const parsedEvent = this.context?.record( + const rawEvent = this.context?.record( PERFORMANCE_NAVIGATION_EVENT_TYPE, event ); - if (parsedEvent && !this.dispatchedLoad) { - this.dispatchedLoad = true; + if (rawEvent) { this.context?.bus.dispatch(PERFORMANCE_NAVIGATION_EVENT_TYPE, { - payload: parsedEvent + payload: rawEvent }); } } From 75a5f6891dd6b45deaf18ffa3ea8a370c7e25d2d Mon Sep 17 00:00:00 2001 From: Billy Date: Fri, 8 Sep 2023 10:41:04 -0700 Subject: [PATCH 13/28] Revert "chore: only publish image resource events" This reverts commit f4f5f7b825cb1efdfebc2a21d3c13db521884fa6. --- src/plugins/event-plugins/ResourcePlugin.ts | 2 +- .../event-plugins/__tests__/ResourcePlugin.test.ts | 9 ++------- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/src/plugins/event-plugins/ResourcePlugin.ts b/src/plugins/event-plugins/ResourcePlugin.ts index 0ff4a99a..993b2d14 100644 --- a/src/plugins/event-plugins/ResourcePlugin.ts +++ b/src/plugins/event-plugins/ResourcePlugin.ts @@ -133,7 +133,7 @@ export class ResourcePlugin extends InternalPlugin { eventData ); - if (parsedEvent && fileType === ResourceType.IMAGE) { + if (parsedEvent) { this.context?.bus.dispatch(PERFORMANCE_RESOURCE_EVENT_TYPE, { key: entryData, payload: parsedEvent diff --git a/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts b/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts index 53dd785a..0e3abfab 100644 --- a/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts @@ -25,7 +25,6 @@ import { PERFORMANCE_RESOURCE_EVENT_TYPE } from '../../utils/constant'; import { ResourceEvent } from '../../../events/resource-event'; import { PluginContext, RecordEvent } from '../../types'; import { PartialPerformancePluginConfig } from 'plugins/utils/performance-utils'; -import { ResourceType } from '../../../utils/common-utils'; const buildResourcePlugin = (config?: PartialPerformancePluginConfig) => { return new ResourcePlugin(config); @@ -266,11 +265,10 @@ describe('ResourcePlugin tests', () => { expect(record).not.toHaveBeenCalled(); }); - test('when image is recorded then it is dispatched', async () => { + test('when entry is recorded then it is dispatched to event bus', async () => { // Setup mockRandom(0); // Retain order in shuffle - mockPerformanceObjectWith([imageResourceEvent], [], []); - mockPerformanceObserver(); + const plugin: ResourcePlugin = buildResourcePlugin(); const record: jest.MockedFunction = jest .fn() @@ -289,9 +287,6 @@ describe('ResourcePlugin tests', () => { expect(record.mock.calls[0][0]).toEqual( PERFORMANCE_RESOURCE_EVENT_TYPE ); - expect((record.mock.calls[0][1] as ResourceEvent).fileType).toEqual( - ResourceType.IMAGE - ); expect(context.bus.dispatch).toHaveBeenCalled(); // eslint-disable-line }); }); From 16f9cdf6f280ee7a6ac08604a74d4e2ca177c639 Mon Sep 17 00:00:00 2001 From: Billy Date: Fri, 8 Sep 2023 10:41:05 -0700 Subject: [PATCH 14/28] Revert "chore: resolve nits" This reverts commit 360f281a20c180b69f5c6d0f035665ad6ac5de77. --- src/event-bus/EventBus.ts | 10 +++------- src/event-bus/__tests__/EventBus.test.ts | 18 ++++++------------ src/event-cache/EventCache.ts | 9 +++------ src/plugins/event-plugins/DemoPlugin.ts | 2 +- src/plugins/event-plugins/NavigationPlugin.ts | 7 ++++--- src/plugins/event-plugins/ResourcePlugin.ts | 9 +++++---- .../__tests__/NavigationPlugin.test.ts | 4 ++-- .../__tests__/ResourcePlugin.test.ts | 4 ++-- src/plugins/types.ts | 4 ++-- 9 files changed, 28 insertions(+), 39 deletions(-) diff --git a/src/event-bus/EventBus.ts b/src/event-bus/EventBus.ts index b70de964..2307ec51 100644 --- a/src/event-bus/EventBus.ts +++ b/src/event-bus/EventBus.ts @@ -1,8 +1,4 @@ -export type Subscriber = (message: any) => void; -export interface Message { - key?: any; - payload: any; -} +export type Subscriber = (payload: any) => void; /** A topic-based event bus to facilitate communication between plugins */ export default class EventBus { @@ -30,11 +26,11 @@ export default class EventBus { return false; } - dispatch(topic: string, message: Message): void { + notify(topic: string, payload: any): void { const list = this.subscribers.get(topic); if (list) { for (const subscriber of list) { - subscriber(message); + subscriber(payload); } } } diff --git a/src/event-bus/__tests__/EventBus.test.ts b/src/event-bus/__tests__/EventBus.test.ts index b013c929..070f43c9 100644 --- a/src/event-bus/__tests__/EventBus.test.ts +++ b/src/event-bus/__tests__/EventBus.test.ts @@ -8,21 +8,17 @@ describe('EventBus tests', () => { eventBus = new EventBus(); jest.clearAllMocks(); }); - test('when dispatch is invoked then all listeners are called', async () => { + test('when notify is invoked then all listeners are called', async () => { // init eventBus.subscribe('food', l1); eventBus.subscribe('food', l2); // run - eventBus.dispatch('food', { key: 'bk', payload: 'whopper' }); + eventBus.notify('food', 'burger'); // assert - expect(l1).toHaveBeenCalledWith( - expect.objectContaining({ key: 'bk', payload: 'whopper' }) - ); - expect(l2).toHaveBeenCalledWith( - expect.objectContaining({ key: 'bk', payload: 'whopper' }) - ); + expect(l1).toHaveBeenCalledWith('burger'); + expect(l2).toHaveBeenCalledWith('burger'); }); test('when listener is removed then it is not called', async () => { @@ -32,12 +28,10 @@ describe('EventBus tests', () => { const removed = eventBus.unsubscribe('food', l2); // run - eventBus.dispatch('food', { payload: 'sushi' }); + eventBus.notify('food', 'burger'); // assert - expect(l1).toHaveBeenCalledWith( - expect.objectContaining({ payload: 'sushi' }) - ); + expect(l1).toHaveBeenCalledWith('burger'); expect(removed).toBe(true); expect(l2).not.toHaveBeenCalled(); }); diff --git a/src/event-cache/EventCache.ts b/src/event-cache/EventCache.ts index 4084ffa3..0490ee23 100644 --- a/src/event-cache/EventCache.ts +++ b/src/event-cache/EventCache.ts @@ -88,12 +88,9 @@ export class EventCache { * If the session is not being recorded, the event will not be recorded. * * @param type The event schema. - * @returns {ParsedRumEvent | undefined} if the event was recorded + * @returns {ParsedRumEvent} if the event was recorded */ - public recordEvent = ( - type: string, - eventData: object - ): ParsedRumEvent | undefined => { + public recordEvent = (type: string, eventData: object) => { if (!this.enabled) { return; } @@ -211,7 +208,7 @@ export class EventCache { private addRecordToCache = ( type: string, eventData: object - ): ParsedRumEvent | undefined => { + ): ParsedRumEvent => { if (!this.enabled) { return; } diff --git a/src/plugins/event-plugins/DemoPlugin.ts b/src/plugins/event-plugins/DemoPlugin.ts index 6b45d178..e7e72f27 100644 --- a/src/plugins/event-plugins/DemoPlugin.ts +++ b/src/plugins/event-plugins/DemoPlugin.ts @@ -19,7 +19,7 @@ export class DemoPlugin implements Plugin { this.configuration = {}; this.timerId = undefined; // eslint-disable-next-line @typescript-eslint/no-empty-function - this.recordEvent = () => undefined; + this.recordEvent = () => {}; } getPluginId(): string { diff --git a/src/plugins/event-plugins/NavigationPlugin.ts b/src/plugins/event-plugins/NavigationPlugin.ts index 3bcfd7ff..b535ee51 100644 --- a/src/plugins/event-plugins/NavigationPlugin.ts +++ b/src/plugins/event-plugins/NavigationPlugin.ts @@ -290,9 +290,10 @@ export class NavigationPlugin extends InternalPlugin { event ); if (rawEvent) { - this.context?.bus.dispatch(PERFORMANCE_NAVIGATION_EVENT_TYPE, { - payload: rawEvent - }); + this.context?.bus.notify( + PERFORMANCE_NAVIGATION_EVENT_TYPE, + rawEvent + ); } } } diff --git a/src/plugins/event-plugins/ResourcePlugin.ts b/src/plugins/event-plugins/ResourcePlugin.ts index 993b2d14..301b69e4 100644 --- a/src/plugins/event-plugins/ResourcePlugin.ts +++ b/src/plugins/event-plugins/ResourcePlugin.ts @@ -11,6 +11,7 @@ import { PartialPerformancePluginConfig, PerformancePluginConfig } from '../utils/performance-utils'; +import { ParsedRumEvent } from 'dispatch/dataplane'; export const RESOURCE_EVENT_PLUGIN_ID = 'resource'; @@ -134,10 +135,10 @@ export class ResourcePlugin extends InternalPlugin { ); if (parsedEvent) { - this.context?.bus.dispatch(PERFORMANCE_RESOURCE_EVENT_TYPE, { - key: entryData, - payload: parsedEvent - }); + this.context?.bus.notify(PERFORMANCE_RESOURCE_EVENT_TYPE, [ + entryData, + parsedEvent + ]); } } }; diff --git a/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts b/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts index b9b65e5f..50629372 100644 --- a/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts @@ -53,7 +53,7 @@ describe('NavigationPlugin tests', () => { navigationType: navigationEvent.type }) ); - expect(context.bus.dispatch).toHaveBeenCalledTimes(1); // eslint-disable-line + expect(context.bus.notify).toHaveBeenCalledTimes(1); // eslint-disable-line }); test('When navigation timing level 2 API is not present then navigation timing level 1 API is recorded', async () => { @@ -93,7 +93,7 @@ describe('NavigationPlugin tests', () => { navigationTimingLevel: 1 }) ); - expect(context.bus.dispatch).toHaveBeenCalledTimes(1); // eslint-disable-line + expect(context.bus.notify).toHaveBeenCalledTimes(1); // eslint-disable-line }); test('when enabled then events are recorded', async () => { diff --git a/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts b/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts index 0e3abfab..fb13c98c 100644 --- a/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts @@ -265,7 +265,7 @@ describe('ResourcePlugin tests', () => { expect(record).not.toHaveBeenCalled(); }); - test('when entry is recorded then it is dispatched to event bus', async () => { + test('when entry is recorded then it is published to event bus', async () => { // Setup mockRandom(0); // Retain order in shuffle @@ -287,6 +287,6 @@ describe('ResourcePlugin tests', () => { expect(record.mock.calls[0][0]).toEqual( PERFORMANCE_RESOURCE_EVENT_TYPE ); - expect(context.bus.dispatch).toHaveBeenCalled(); // eslint-disable-line + expect(context.bus.notify).toHaveBeenCalled(); // eslint-disable-line }); }); diff --git a/src/plugins/types.ts b/src/plugins/types.ts index 9891a402..445f56b2 100644 --- a/src/plugins/types.ts +++ b/src/plugins/types.ts @@ -1,12 +1,12 @@ import { Config } from '../orchestration/Orchestration'; import { Session } from '../sessions/SessionManager'; import EventBus from '../event-bus/EventBus'; -import { ParsedRumEvent } from '../dispatch/dataplane'; +import { ParsedRumEvent } from 'dispatch/dataplane'; export type RecordEvent = ( type: string, eventData: object -) => ParsedRumEvent | undefined; +) => ParsedRumEvent | void; export type RecordPageView = (pageId: string) => void; export type GetSession = () => Session | undefined; From a6be0928ba8a1f15d57e9be7b4c3ef6d1debdb8f Mon Sep 17 00:00:00 2001 From: Billy Date: Fri, 8 Sep 2023 10:41:06 -0700 Subject: [PATCH 15/28] Revert "chore: publish navigation events" This reverts commit 10d1854db7f000953287e9c419c1cef899dbe8b3. --- src/plugins/event-plugins/NavigationPlugin.ts | 28 ++++++++----------- .../__tests__/NavigationPlugin.test.ts | 25 +++-------------- 2 files changed, 16 insertions(+), 37 deletions(-) diff --git a/src/plugins/event-plugins/NavigationPlugin.ts b/src/plugins/event-plugins/NavigationPlugin.ts index b535ee51..788ebe92 100644 --- a/src/plugins/event-plugins/NavigationPlugin.ts +++ b/src/plugins/event-plugins/NavigationPlugin.ts @@ -178,7 +178,12 @@ export class NavigationPlugin extends InternalPlugin { duration: entryData.loadEventEnd - entryData.navigationStart, navigationTimingLevel: 1 }; - this.recordEvent(eventDataNavigationTimingLevel1); + if (this.context?.record) { + this.context.record( + PERFORMANCE_NAVIGATION_EVENT_TYPE, + eventDataNavigationTimingLevel1 + ); + } }; // Timeout is required for loadEventEnd to complete setTimeout(recordNavigation, 0); @@ -257,7 +262,12 @@ export class NavigationPlugin extends InternalPlugin { navigationTimingLevel: 2 }; - this.recordEvent(eventDataNavigationTimingLevel2); + if (this.context?.record) { + this.context.record( + PERFORMANCE_NAVIGATION_EVENT_TYPE, + eventDataNavigationTimingLevel2 + ); + } }; /** @@ -282,18 +292,4 @@ export class NavigationPlugin extends InternalPlugin { } } } - - /** Record event and publish if record was successful */ - private recordEvent(event: NavigationEvent) { - const rawEvent = this.context?.record( - PERFORMANCE_NAVIGATION_EVENT_TYPE, - event - ); - if (rawEvent) { - this.context?.bus.notify( - PERFORMANCE_NAVIGATION_EVENT_TYPE, - rawEvent - ); - } - } } diff --git a/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts b/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts index 50629372..6e40578e 100644 --- a/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts @@ -5,14 +5,12 @@ import { mockPerformanceObserver, MockPerformanceTiming, mockPerformanceObjectWith, - putRumEventsDocument, - parsedRumEvent + putRumEventsDocument } from '../../../test-utils/mock-data'; import { NavigationPlugin } from '../NavigationPlugin'; import { context, record } from '../../../test-utils/test-utils'; import { PERFORMANCE_NAVIGATION_EVENT_TYPE } from '../../utils/constant'; import { PartialPerformancePluginConfig } from 'plugins/utils/performance-utils'; -import { RecordEvent } from 'plugins/types'; const buildNavigationPlugin = (config?: PartialPerformancePluginConfig) => { return new NavigationPlugin(config); @@ -32,14 +30,7 @@ describe('NavigationPlugin tests', () => { test('When navigation event is present then event is recorded', async () => { const plugin: NavigationPlugin = buildNavigationPlugin(); // Run - const record: jest.MockedFunction = jest - .fn() - .mockReturnValue({ ...parsedRumEvent }); - const mockContext = { - ...context, - record - }; - plugin.load(mockContext); + plugin.load(context); window.dispatchEvent(new Event('load')); plugin.disable(); @@ -53,7 +44,6 @@ describe('NavigationPlugin tests', () => { navigationType: navigationEvent.type }) ); - expect(context.bus.notify).toHaveBeenCalledTimes(1); // eslint-disable-line }); test('When navigation timing level 2 API is not present then navigation timing level 1 API is recorded', async () => { @@ -62,14 +52,8 @@ describe('NavigationPlugin tests', () => { mockPerformanceObserver(); const plugin: NavigationPlugin = buildNavigationPlugin(); - const record: jest.MockedFunction = jest - .fn() - .mockReturnValue({ ...parsedRumEvent }); - const mockContext = { - ...context, - record - }; - plugin.load(mockContext); + + plugin.load(context); window.dispatchEvent(new Event('load')); plugin.disable(); @@ -93,7 +77,6 @@ describe('NavigationPlugin tests', () => { navigationTimingLevel: 1 }) ); - expect(context.bus.notify).toHaveBeenCalledTimes(1); // eslint-disable-line }); test('when enabled then events are recorded', async () => { From 1bdd317148666f6463321e2610d6519ab110ff73 Mon Sep 17 00:00:00 2001 From: Billy Date: Fri, 8 Sep 2023 10:41:07 -0700 Subject: [PATCH 16/28] Revert "chore: publish resource events" This reverts commit 388c1cc11eeb2335fbe61736a233f63558a641d8. --- src/event-cache/EventCache.ts | 1 - src/event-cache/__tests__/EventCache.test.ts | 6 +--- src/plugins/event-plugins/ResourcePlugin.ts | 16 ++-------- .../__tests__/ResourcePlugin.test.ts | 30 ++----------------- src/test-utils/mock-data.ts | 16 ---------- 5 files changed, 5 insertions(+), 64 deletions(-) diff --git a/src/event-cache/EventCache.ts b/src/event-cache/EventCache.ts index 0490ee23..85c9ebf0 100644 --- a/src/event-cache/EventCache.ts +++ b/src/event-cache/EventCache.ts @@ -88,7 +88,6 @@ export class EventCache { * If the session is not being recorded, the event will not be recorded. * * @param type The event schema. - * @returns {ParsedRumEvent} if the event was recorded */ public recordEvent = (type: string, eventData: object) => { if (!this.enabled) { diff --git a/src/event-cache/__tests__/EventCache.test.ts b/src/event-cache/__tests__/EventCache.test.ts index 66fd3610..39217b55 100644 --- a/src/event-cache/__tests__/EventCache.test.ts +++ b/src/event-cache/__tests__/EventCache.test.ts @@ -3,11 +3,7 @@ import { advanceTo } from 'jest-date-mock'; import * as Utils from '../../test-utils/test-utils'; import { SessionManager } from '../../sessions/SessionManager'; import { RumEvent } from '../../dispatch/dataplane'; -import { - DEFAULT_CONFIG, - WEB_CLIENT_VERSION, - mockFetch -} from '../../test-utils/test-utils'; +import { DEFAULT_CONFIG, mockFetch } from '../../test-utils/test-utils'; import { INSTALL_MODULE, INSTALL_SCRIPT } from '../../utils/constants'; global.fetch = mockFetch; diff --git a/src/plugins/event-plugins/ResourcePlugin.ts b/src/plugins/event-plugins/ResourcePlugin.ts index 301b69e4..6d0c35ac 100644 --- a/src/plugins/event-plugins/ResourcePlugin.ts +++ b/src/plugins/event-plugins/ResourcePlugin.ts @@ -11,7 +11,6 @@ import { PartialPerformancePluginConfig, PerformancePluginConfig } from '../utils/performance-utils'; -import { ParsedRumEvent } from 'dispatch/dataplane'; export const RESOURCE_EVENT_PLUGIN_ID = 'resource'; @@ -118,28 +117,17 @@ export class ResourcePlugin extends InternalPlugin { } if (this.context?.record) { - const fileType = getResourceFileType(entryData.name); const eventData: ResourceEvent = { version: '1.0.0', initiatorType: entryData.initiatorType, duration: entryData.duration, - fileType, + fileType: getResourceFileType(entryData.name), transferSize: entryData.transferSize }; if (this.context.config.recordResourceUrl) { eventData.targetUrl = entryData.name; } - const parsedEvent = this.context.record( - PERFORMANCE_RESOURCE_EVENT_TYPE, - eventData - ); - - if (parsedEvent) { - this.context?.bus.notify(PERFORMANCE_RESOURCE_EVENT_TYPE, [ - entryData, - parsedEvent - ]); - } + this.context.record(PERFORMANCE_RESOURCE_EVENT_TYPE, eventData); } }; diff --git a/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts b/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts index fb13c98c..26fd8a91 100644 --- a/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts @@ -9,8 +9,7 @@ import { mockPerformanceObjectWith, putRumEventsDocument, putRumEventsGammaDocument, - dataPlaneDocument, - parsedRumEvent + dataPlaneDocument } from '../../../test-utils/mock-data'; import { ResourcePlugin } from '../ResourcePlugin'; import { mockRandom } from 'jest-mock-random'; @@ -23,7 +22,7 @@ import { } from '../../../test-utils/test-utils'; import { PERFORMANCE_RESOURCE_EVENT_TYPE } from '../../utils/constant'; import { ResourceEvent } from '../../../events/resource-event'; -import { PluginContext, RecordEvent } from '../../types'; +import { PluginContext } from '../../types'; import { PartialPerformancePluginConfig } from 'plugins/utils/performance-utils'; const buildResourcePlugin = (config?: PartialPerformancePluginConfig) => { @@ -264,29 +263,4 @@ describe('ResourcePlugin tests', () => { expect(record).not.toHaveBeenCalled(); }); - - test('when entry is recorded then it is published to event bus', async () => { - // Setup - mockRandom(0); // Retain order in shuffle - - const plugin: ResourcePlugin = buildResourcePlugin(); - const record: jest.MockedFunction = jest - .fn() - .mockReturnValue({ ...parsedRumEvent }); - const mockContext = { - ...context, - record - }; - - // Run - plugin.load(mockContext); - window.dispatchEvent(new Event('load')); - plugin.disable(); - - // Assert - expect(record.mock.calls[0][0]).toEqual( - PERFORMANCE_RESOURCE_EVENT_TYPE - ); - expect(context.bus.notify).toHaveBeenCalled(); // eslint-disable-line - }); }); diff --git a/src/test-utils/mock-data.ts b/src/test-utils/mock-data.ts index 79299f8d..bf1b2348 100644 --- a/src/test-utils/mock-data.ts +++ b/src/test-utils/mock-data.ts @@ -1,7 +1,3 @@ -/* eslint max-classes-per-file: 0 */ -import { INSTALL_MODULE } from '../utils/constants'; -import { WEB_CLIENT_VERSION } from './test-utils'; - /* eslint-disable max-classes-per-file */ export const firstPaintEvent = { name: 'first-paint', @@ -462,15 +458,3 @@ export const httpErrorEvent = { 'https://jtrm21au2a.execute-api.us-west-2.amazonaws.com/alpha/v1.0.0/putBatchMetrics', responseText: '{"message":"Could not persist data"}' }; - -export const parsedRumEvent = { - id: expect.stringMatching(/[0-9a-f\-]+/), - timestamp: new Date(), - type: 'com.amazon.rum.event', - metadata: { - version: '1.0.0', - 'aws:client': INSTALL_MODULE, - 'aws:clientVersion': WEB_CLIENT_VERSION - }, - details: {} -}; From df7aeed6390210a99dd3bfa5c2188f3730f2ad59 Mon Sep 17 00:00:00 2001 From: Billy Date: Fri, 8 Sep 2023 10:41:35 -0700 Subject: [PATCH 17/28] Revert "fix: EventCache.record returns ParsedRumEvent without publishing" This reverts commit c2d81eb8e5ca09f09a76388f383a60a4ab5cc808. --- src/dispatch/dataplane.ts | 10 ---- src/event-cache/EventCache.ts | 25 ++++----- src/event-cache/__tests__/EventCache.test.ts | 56 ++++++++++++++------ src/plugins/types.ts | 6 +-- 4 files changed, 51 insertions(+), 46 deletions(-) diff --git a/src/dispatch/dataplane.ts b/src/dispatch/dataplane.ts index 742413b9..06a8d1ed 100644 --- a/src/dispatch/dataplane.ts +++ b/src/dispatch/dataplane.ts @@ -5,8 +5,6 @@ // use the type definitions from the CloudWatch RUM SDK, we have made a copy of // them here to completely remove the dependency on the CloudWatch RUM SDK. -import { MetaData } from '../events/meta-data'; - export interface PutRumEventsRequest { BatchId: string; AppMonitorDetails: AppMonitorDetails; @@ -31,11 +29,3 @@ export interface RumEvent { metadata?: string; details: string; } - -export interface ParsedRumEvent { - id: string; - timestamp: Date; - type: string; - metadata?: MetaData; - details: object; -} diff --git a/src/event-cache/EventCache.ts b/src/event-cache/EventCache.ts index 85c9ebf0..bc404f6b 100644 --- a/src/event-cache/EventCache.ts +++ b/src/event-cache/EventCache.ts @@ -6,8 +6,7 @@ import { PageAttributes, PageManager } from '../sessions/PageManager'; import { AppMonitorDetails, UserDetails, - RumEvent, - ParsedRumEvent + RumEvent } from '../dispatch/dataplane'; import EventBus from '../event-bus/EventBus'; @@ -99,7 +98,7 @@ export class EventCache { this.sessionManager.incrementSessionEventCount(); if (this.canRecord(session)) { - return this.addRecordToCache(type, eventData); + this.addRecordToCache(type, eventData); } } }; @@ -204,10 +203,7 @@ export class EventCache { * * @param type The event schema. */ - private addRecordToCache = ( - type: string, - eventData: object - ): ParsedRumEvent => { + private addRecordToCache = (type: string, eventData: object) => { if (!this.enabled) { return; } @@ -229,22 +225,21 @@ export class EventCache { 'aws:clientVersion': webClientVersion }; - const partial = { + const partialEvent = { id: v4(), timestamp: new Date(), type }; - + this.bus.notify(type, { + ...partialEvent, + details: eventData, + metadata: metaData + }); this.events.push({ - ...partial, + ...partialEvent, details: JSON.stringify(eventData), metadata: JSON.stringify(metaData) }); - return { - ...partial, - details: eventData, - metadata: metaData - }; }; /** diff --git a/src/event-cache/__tests__/EventCache.test.ts b/src/event-cache/__tests__/EventCache.test.ts index 39217b55..481d506b 100644 --- a/src/event-cache/__tests__/EventCache.test.ts +++ b/src/event-cache/__tests__/EventCache.test.ts @@ -5,6 +5,8 @@ import { SessionManager } from '../../sessions/SessionManager'; import { RumEvent } from '../../dispatch/dataplane'; import { DEFAULT_CONFIG, mockFetch } from '../../test-utils/test-utils'; import { INSTALL_MODULE, INSTALL_SCRIPT } from '../../utils/constants'; +import EventBus from '../../event-bus/EventBus'; +jest.mock('../../event-bus/EventBus'); global.fetch = mockFetch; const getSession = jest.fn(() => ({ @@ -492,10 +494,14 @@ describe('EventCache tests', () => { expect(eventCache.getEventBatch().length).toEqual(1); }); - test('when event is recorded then the parsed event is returned', async () => { + test('when event is recorded then subscribers are notified with raw event', async () => { // Init const EVENT1_SCHEMA = 'com.amazon.rum.event1'; - const eventCache: EventCache = Utils.createDefaultEventCache(); + const bus = new EventBus(); + const eventCache: EventCache = Utils.createEventCache( + DEFAULT_CONFIG, + bus + ); const event = { id: expect.stringMatching(/[0-9a-f\-]+/), @@ -505,23 +511,41 @@ describe('EventCache tests', () => { details: '{}' }; - const parsedEventMatcher = { - id: expect.stringMatching(/[0-9a-f\-]+/), - timestamp: new Date(), - type: EVENT1_SCHEMA, - metadata: expect.objectContaining({ - version: '1.0.0', - 'aws:client': INSTALL_MODULE, - 'aws:clientVersion': WEB_CLIENT_VERSION - }), - details: expect.objectContaining({}) - }; - // Run - const parsedEvent = eventCache.recordEvent(EVENT1_SCHEMA, {}); + eventCache.recordEvent(EVENT1_SCHEMA, {}); const eventBatch: RumEvent[] = eventCache.getEventBatch(); expect(eventBatch).toEqual(expect.arrayContaining([event])); - expect(parsedEvent).toEqual(parsedEventMatcher); + // eslint-disable-next-line + expect(bus.notify).toHaveBeenCalledWith( + EVENT1_SCHEMA, + expect.objectContaining({ + id: expect.stringMatching(/[0-9a-f\-]+/), + timestamp: new Date(), + type: EVENT1_SCHEMA, + metadata: expect.objectContaining({ + version: '1.0.0', + 'aws:client': INSTALL_MODULE, + 'aws:clientVersion': WEB_CLIENT_VERSION + }), + details: expect.objectContaining({}) + }) + ); + }); + + test('when cache is disabled then subscribers are not notified', async () => { + // Init + const EVENT1_SCHEMA = 'com.amazon.rum.event1'; + const bus = new EventBus(); + const eventCache: EventCache = Utils.createEventCache( + DEFAULT_CONFIG, + bus + ); + // Run + eventCache.disable(); + eventCache.recordEvent(EVENT1_SCHEMA, {}); + const eventBatch: RumEvent[] = eventCache.getEventBatch(); + expect(eventBatch).toHaveLength(0); + expect(bus.notify).not.toHaveBeenCalled(); // eslint-disable-line }); test('when event limit is zero then recordEvent records all events', async () => { diff --git a/src/plugins/types.ts b/src/plugins/types.ts index 445f56b2..aa3c7d9b 100644 --- a/src/plugins/types.ts +++ b/src/plugins/types.ts @@ -1,12 +1,8 @@ import { Config } from '../orchestration/Orchestration'; import { Session } from '../sessions/SessionManager'; import EventBus from '../event-bus/EventBus'; -import { ParsedRumEvent } from 'dispatch/dataplane'; -export type RecordEvent = ( - type: string, - eventData: object -) => ParsedRumEvent | void; +export type RecordEvent = (type: string, eventData: object) => void; export type RecordPageView = (pageId: string) => void; export type GetSession = () => Session | undefined; From d6a94979c122e60b0c60b040bac577f5ccaf4e90 Mon Sep 17 00:00:00 2001 From: Billy Date: Fri, 8 Sep 2023 10:46:26 -0700 Subject: [PATCH 18/28] refactor: rename notify to dispatch --- src/event-bus/EventBus.ts | 2 +- src/event-bus/__tests__/EventBus.test.ts | 6 +++--- src/event-cache/EventCache.ts | 2 +- src/event-cache/__tests__/EventCache.test.ts | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/event-bus/EventBus.ts b/src/event-bus/EventBus.ts index 2307ec51..5de21267 100644 --- a/src/event-bus/EventBus.ts +++ b/src/event-bus/EventBus.ts @@ -26,7 +26,7 @@ export default class EventBus { return false; } - notify(topic: string, payload: any): void { + dispatch(topic: string, payload: any): void { const list = this.subscribers.get(topic); if (list) { for (const subscriber of list) { diff --git a/src/event-bus/__tests__/EventBus.test.ts b/src/event-bus/__tests__/EventBus.test.ts index 070f43c9..7e86eb4f 100644 --- a/src/event-bus/__tests__/EventBus.test.ts +++ b/src/event-bus/__tests__/EventBus.test.ts @@ -8,13 +8,13 @@ describe('EventBus tests', () => { eventBus = new EventBus(); jest.clearAllMocks(); }); - test('when notify is invoked then all listeners are called', async () => { + test('when dispatch is invoked then all listeners are called', async () => { // init eventBus.subscribe('food', l1); eventBus.subscribe('food', l2); // run - eventBus.notify('food', 'burger'); + eventBus.dispatch('food', 'burger'); // assert expect(l1).toHaveBeenCalledWith('burger'); @@ -28,7 +28,7 @@ describe('EventBus tests', () => { const removed = eventBus.unsubscribe('food', l2); // run - eventBus.notify('food', 'burger'); + eventBus.dispatch('food', 'burger'); // assert expect(l1).toHaveBeenCalledWith('burger'); diff --git a/src/event-cache/EventCache.ts b/src/event-cache/EventCache.ts index bc404f6b..50c4e386 100644 --- a/src/event-cache/EventCache.ts +++ b/src/event-cache/EventCache.ts @@ -230,7 +230,7 @@ export class EventCache { timestamp: new Date(), type }; - this.bus.notify(type, { + this.bus.dispatch(type, { ...partialEvent, details: eventData, metadata: metaData diff --git a/src/event-cache/__tests__/EventCache.test.ts b/src/event-cache/__tests__/EventCache.test.ts index 481d506b..047828dd 100644 --- a/src/event-cache/__tests__/EventCache.test.ts +++ b/src/event-cache/__tests__/EventCache.test.ts @@ -516,7 +516,7 @@ describe('EventCache tests', () => { const eventBatch: RumEvent[] = eventCache.getEventBatch(); expect(eventBatch).toEqual(expect.arrayContaining([event])); // eslint-disable-next-line - expect(bus.notify).toHaveBeenCalledWith( + expect(bus.dispatch).toHaveBeenCalledWith( EVENT1_SCHEMA, expect.objectContaining({ id: expect.stringMatching(/[0-9a-f\-]+/), @@ -545,7 +545,7 @@ describe('EventCache tests', () => { eventCache.recordEvent(EVENT1_SCHEMA, {}); const eventBatch: RumEvent[] = eventCache.getEventBatch(); expect(eventBatch).toHaveLength(0); - expect(bus.notify).not.toHaveBeenCalled(); // eslint-disable-line + expect(bus.dispatch).not.toHaveBeenCalled(); // eslint-disable-line }); test('when event limit is zero then recordEvent records all events', async () => { From 202a919f74faa72d9c5c035f608af0659ad696d7 Mon Sep 17 00:00:00 2001 From: Billy Date: Fri, 8 Sep 2023 10:49:59 -0700 Subject: [PATCH 19/28] chore: rename context.bus to eventBus --- src/event-cache/EventCache.ts | 4 ++-- src/orchestration/Orchestration.ts | 4 ++-- src/plugins/types.ts | 2 +- src/test-utils/test-utils.ts | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/event-cache/EventCache.ts b/src/event-cache/EventCache.ts index 50c4e386..046d4b3b 100644 --- a/src/event-cache/EventCache.ts +++ b/src/event-cache/EventCache.ts @@ -41,7 +41,7 @@ export class EventCache { constructor( applicationDetails: AppMonitorDetails, config: Config, - private bus = new EventBus() + private eventBus = new EventBus() ) { this.appMonitorDetails = applicationDetails; this.config = config; @@ -230,7 +230,7 @@ export class EventCache { timestamp: new Date(), type }; - this.bus.dispatch(type, { + this.eventBus.dispatch(type, { ...partialEvent, details: eventData, metadata: metaData diff --git a/src/orchestration/Orchestration.ts b/src/orchestration/Orchestration.ts index 9df001d7..8a412f4e 100644 --- a/src/orchestration/Orchestration.ts +++ b/src/orchestration/Orchestration.ts @@ -207,7 +207,7 @@ export class Orchestration { private eventCache: EventCache; private dispatchManager: Dispatch; private config: Config; - private bus = new EventBus(); + private eventBus = new EventBus(); /** * Instantiate the CloudWatch RUM web client and begin monitoring the @@ -447,7 +447,7 @@ export class Orchestration { record: this.eventCache.recordEvent, recordPageView: this.eventCache.recordPageView, getSession: this.eventCache.getSession, - bus: this.bus + eventBus: this.eventBus }; // Initialize PluginManager diff --git a/src/plugins/types.ts b/src/plugins/types.ts index aa3c7d9b..4b9f8b35 100644 --- a/src/plugins/types.ts +++ b/src/plugins/types.ts @@ -13,5 +13,5 @@ export type PluginContext = { record: RecordEvent; recordPageView: RecordPageView; getSession: GetSession; - bus: EventBus; + eventBus: EventBus; }; diff --git a/src/test-utils/test-utils.ts b/src/test-utils/test-utils.ts index 2bf95e53..592c0136 100644 --- a/src/test-utils/test-utils.ts +++ b/src/test-utils/test-utils.ts @@ -118,7 +118,7 @@ export const context: PluginContext = { record, recordPageView, getSession, - bus: new EventBus() + eventBus: new EventBus() }; export const xRayOffContext: PluginContext = { From 90fab11373ddca35ac2dc29e3cf5b50ac11b8d77 Mon Sep 17 00:00:00 2001 From: Billy Date: Fri, 8 Sep 2023 11:22:48 -0700 Subject: [PATCH 20/28] chore: add topic enum to eventbus --- src/event-bus/EventBus.ts | 13 +++++--- src/event-bus/__tests__/EventBus.test.ts | 35 ++++++++++++++------ src/event-cache/EventCache.ts | 6 ++-- src/event-cache/__tests__/EventCache.test.ts | 6 ++-- src/orchestration/Orchestration.ts | 2 +- src/plugins/types.ts | 4 +-- src/test-utils/test-utils.ts | 2 +- 7 files changed, 43 insertions(+), 25 deletions(-) diff --git a/src/event-bus/EventBus.ts b/src/event-bus/EventBus.ts index 5de21267..09dbb217 100644 --- a/src/event-bus/EventBus.ts +++ b/src/event-bus/EventBus.ts @@ -1,11 +1,14 @@ export type Subscriber = (payload: any) => void; +export enum Topic { + EVENTS = 'events' +} /** A topic-based event bus to facilitate communication between plugins */ -export default class EventBus { +export default class EventBus { // map - private subscribers = new Map(); + private subscribers = new Map(); - subscribe(topic: string, subscriber: Subscriber): void { + subscribe(topic: T, subscriber: Subscriber): void { const list = this.subscribers.get(topic) ?? []; if (list.length === 0) { this.subscribers.set(topic, list); @@ -13,7 +16,7 @@ export default class EventBus { list.push(subscriber); } - unsubscribe(topic: string, subscriber: Subscriber) { + unsubscribe(topic: T, subscriber: Subscriber) { const list = this.subscribers.get(topic); if (list) { for (let i = 0; i < list.length; i++) { @@ -26,7 +29,7 @@ export default class EventBus { return false; } - dispatch(topic: string, payload: any): void { + dispatch(topic: T, payload: any): void { const list = this.subscribers.get(topic); if (list) { for (const subscriber of list) { diff --git a/src/event-bus/__tests__/EventBus.test.ts b/src/event-bus/__tests__/EventBus.test.ts index 7e86eb4f..02645476 100644 --- a/src/event-bus/__tests__/EventBus.test.ts +++ b/src/event-bus/__tests__/EventBus.test.ts @@ -1,7 +1,11 @@ -import EventBus from '../EventBus'; +import EventBus, { Topic } from '../EventBus'; +export enum MockTopics { + FOOD = 'food', + BOOKS = 'books' +} describe('EventBus tests', () => { - let eventBus: EventBus; + let eventBus: EventBus; const l1 = jest.fn(); const l2 = jest.fn(); beforeEach(() => { @@ -10,29 +14,40 @@ describe('EventBus tests', () => { }); test('when dispatch is invoked then all listeners are called', async () => { // init - eventBus.subscribe('food', l1); - eventBus.subscribe('food', l2); + eventBus.subscribe(MockTopics.FOOD, l1); + eventBus.subscribe(MockTopics.FOOD, l2); // run - eventBus.dispatch('food', 'burger'); + eventBus.dispatch(MockTopics.FOOD, 'burger'); // assert expect(l1).toHaveBeenCalledWith('burger'); expect(l2).toHaveBeenCalledWith('burger'); }); - test('when listener is removed then it is not called', async () => { + test('when subscriber is removed then it is not called', async () => { // init - eventBus.subscribe('food', l1); - eventBus.subscribe('food', l2); - const removed = eventBus.unsubscribe('food', l2); + eventBus.subscribe(MockTopics.FOOD, l1); + eventBus.subscribe(MockTopics.FOOD, l2); + const removed = eventBus.unsubscribe(MockTopics.FOOD, l2); // run - eventBus.dispatch('food', 'burger'); + eventBus.dispatch(MockTopics.FOOD, 'burger'); // assert expect(l1).toHaveBeenCalledWith('burger'); expect(removed).toBe(true); expect(l2).not.toHaveBeenCalled(); }); + + test('when subscribed to topic A then does not hear topic B', async () => { + eventBus.subscribe(MockTopics.FOOD, l1); + eventBus.subscribe(MockTopics.BOOKS, l2); + + // run + eventBus.dispatch(MockTopics.FOOD, 'burger'); + + // assert + expect(l2).not.toHaveBeenCalled(); + }); }); diff --git a/src/event-cache/EventCache.ts b/src/event-cache/EventCache.ts index 046d4b3b..87f9af53 100644 --- a/src/event-cache/EventCache.ts +++ b/src/event-cache/EventCache.ts @@ -8,7 +8,7 @@ import { UserDetails, RumEvent } from '../dispatch/dataplane'; -import EventBus from '../event-bus/EventBus'; +import EventBus, { Topic } from '../event-bus/EventBus'; const webClientVersion = '1.14.0'; @@ -41,7 +41,7 @@ export class EventCache { constructor( applicationDetails: AppMonitorDetails, config: Config, - private eventBus = new EventBus() + private eventBus = new EventBus() ) { this.appMonitorDetails = applicationDetails; this.config = config; @@ -230,7 +230,7 @@ export class EventCache { timestamp: new Date(), type }; - this.eventBus.dispatch(type, { + this.eventBus.dispatch(Topic.EVENTS, { ...partialEvent, details: eventData, metadata: metaData diff --git a/src/event-cache/__tests__/EventCache.test.ts b/src/event-cache/__tests__/EventCache.test.ts index 047828dd..501faf05 100644 --- a/src/event-cache/__tests__/EventCache.test.ts +++ b/src/event-cache/__tests__/EventCache.test.ts @@ -5,7 +5,7 @@ import { SessionManager } from '../../sessions/SessionManager'; import { RumEvent } from '../../dispatch/dataplane'; import { DEFAULT_CONFIG, mockFetch } from '../../test-utils/test-utils'; import { INSTALL_MODULE, INSTALL_SCRIPT } from '../../utils/constants'; -import EventBus from '../../event-bus/EventBus'; +import EventBus, { Topic } from '../../event-bus/EventBus'; jest.mock('../../event-bus/EventBus'); global.fetch = mockFetch; @@ -494,7 +494,7 @@ describe('EventCache tests', () => { expect(eventCache.getEventBatch().length).toEqual(1); }); - test('when event is recorded then subscribers are notified with raw event', async () => { + test('when event is recorded then events subscribers are notified with raw event', async () => { // Init const EVENT1_SCHEMA = 'com.amazon.rum.event1'; const bus = new EventBus(); @@ -517,7 +517,7 @@ describe('EventCache tests', () => { expect(eventBatch).toEqual(expect.arrayContaining([event])); // eslint-disable-next-line expect(bus.dispatch).toHaveBeenCalledWith( - EVENT1_SCHEMA, + Topic.EVENTS, expect.objectContaining({ id: expect.stringMatching(/[0-9a-f\-]+/), timestamp: new Date(), diff --git a/src/orchestration/Orchestration.ts b/src/orchestration/Orchestration.ts index 8a412f4e..14ed3641 100644 --- a/src/orchestration/Orchestration.ts +++ b/src/orchestration/Orchestration.ts @@ -24,7 +24,7 @@ import { FetchPlugin } from '../plugins/event-plugins/FetchPlugin'; import { PageViewPlugin } from '../plugins/event-plugins/PageViewPlugin'; import { PageAttributes } from '../sessions/PageManager'; import { INSTALL_MODULE } from '../utils/constants'; -import EventBus from '../event-bus/EventBus'; +import EventBus, { Topic } from '../event-bus/EventBus'; const DEFAULT_REGION = 'us-west-2'; const DEFAULT_ENDPOINT = `https://dataplane.rum.${DEFAULT_REGION}.amazonaws.com`; diff --git a/src/plugins/types.ts b/src/plugins/types.ts index 4b9f8b35..670b52de 100644 --- a/src/plugins/types.ts +++ b/src/plugins/types.ts @@ -1,6 +1,6 @@ import { Config } from '../orchestration/Orchestration'; import { Session } from '../sessions/SessionManager'; -import EventBus from '../event-bus/EventBus'; +import EventBus, { Topic } from '../event-bus/EventBus'; export type RecordEvent = (type: string, eventData: object) => void; export type RecordPageView = (pageId: string) => void; @@ -13,5 +13,5 @@ export type PluginContext = { record: RecordEvent; recordPageView: RecordPageView; getSession: GetSession; - eventBus: EventBus; + eventBus: EventBus; }; diff --git a/src/test-utils/test-utils.ts b/src/test-utils/test-utils.ts index 592c0136..04e642da 100644 --- a/src/test-utils/test-utils.ts +++ b/src/test-utils/test-utils.ts @@ -17,7 +17,7 @@ import { UserDetails } from '../dispatch/dataplane'; import { ReadableStream } from 'web-streams-polyfill'; -import EventBus from '../event-bus/EventBus'; +import EventBus, { Topic } from '../event-bus/EventBus'; jest.mock('../event-bus/EventBus'); export const AWS_RUM_ENDPOINT = new URL( From aa7513ff35d0c7dcf1e31589367683c091afb139 Mon Sep 17 00:00:00 2001 From: Billy Date: Fri, 8 Sep 2023 11:38:53 -0700 Subject: [PATCH 21/28] chore: event bus dispatches with optional key --- src/event-bus/EventBus.ts | 10 ++-- src/event-bus/__tests__/EventBus.test.ts | 12 ++--- src/event-cache/EventCache.ts | 15 +++--- src/event-cache/__tests__/EventCache.test.ts | 55 ++++++++++++++++---- src/plugins/types.ts | 2 +- 5 files changed, 69 insertions(+), 25 deletions(-) diff --git a/src/event-bus/EventBus.ts b/src/event-bus/EventBus.ts index 09dbb217..caf6cf77 100644 --- a/src/event-bus/EventBus.ts +++ b/src/event-bus/EventBus.ts @@ -1,4 +1,8 @@ -export type Subscriber = (payload: any) => void; +export type Subscriber = (message: Message) => void; +export interface Message { + key?: any; + payload: any; +} export enum Topic { EVENTS = 'events' } @@ -29,11 +33,11 @@ export default class EventBus { return false; } - dispatch(topic: T, payload: any): void { + dispatch(topic: T, message: Message): void { const list = this.subscribers.get(topic); if (list) { for (const subscriber of list) { - subscriber(payload); + subscriber(message); } } } diff --git a/src/event-bus/__tests__/EventBus.test.ts b/src/event-bus/__tests__/EventBus.test.ts index 02645476..63f6fb0c 100644 --- a/src/event-bus/__tests__/EventBus.test.ts +++ b/src/event-bus/__tests__/EventBus.test.ts @@ -18,11 +18,11 @@ describe('EventBus tests', () => { eventBus.subscribe(MockTopics.FOOD, l2); // run - eventBus.dispatch(MockTopics.FOOD, 'burger'); + eventBus.dispatch(MockTopics.FOOD, { payload: 'burger' }); // assert - expect(l1).toHaveBeenCalledWith('burger'); - expect(l2).toHaveBeenCalledWith('burger'); + expect(l1).toHaveBeenCalledWith({ payload: 'burger' }); + expect(l2).toHaveBeenCalledWith({ payload: 'burger' }); }); test('when subscriber is removed then it is not called', async () => { @@ -32,10 +32,10 @@ describe('EventBus tests', () => { const removed = eventBus.unsubscribe(MockTopics.FOOD, l2); // run - eventBus.dispatch(MockTopics.FOOD, 'burger'); + eventBus.dispatch(MockTopics.FOOD, { payload: 'burger' }); // assert - expect(l1).toHaveBeenCalledWith('burger'); + expect(l1).toHaveBeenCalledWith({ payload: 'burger' }); expect(removed).toBe(true); expect(l2).not.toHaveBeenCalled(); }); @@ -45,7 +45,7 @@ describe('EventBus tests', () => { eventBus.subscribe(MockTopics.BOOKS, l2); // run - eventBus.dispatch(MockTopics.FOOD, 'burger'); + eventBus.dispatch(MockTopics.FOOD, { payload: 'burger' }); // assert expect(l2).not.toHaveBeenCalled(); diff --git a/src/event-cache/EventCache.ts b/src/event-cache/EventCache.ts index 87f9af53..3eb1e2c9 100644 --- a/src/event-cache/EventCache.ts +++ b/src/event-cache/EventCache.ts @@ -88,7 +88,7 @@ export class EventCache { * * @param type The event schema. */ - public recordEvent = (type: string, eventData: object) => { + public recordEvent = (type: string, eventData: object, key?: any) => { if (!this.enabled) { return; } @@ -98,7 +98,7 @@ export class EventCache { this.sessionManager.incrementSessionEventCount(); if (this.canRecord(session)) { - this.addRecordToCache(type, eventData); + this.addRecordToCache(type, eventData, key); } } }; @@ -203,7 +203,7 @@ export class EventCache { * * @param type The event schema. */ - private addRecordToCache = (type: string, eventData: object) => { + private addRecordToCache = (type: string, eventData: object, key?: any) => { if (!this.enabled) { return; } @@ -231,9 +231,12 @@ export class EventCache { type }; this.eventBus.dispatch(Topic.EVENTS, { - ...partialEvent, - details: eventData, - metadata: metaData + ...(key && { key }), + payload: { + ...partialEvent, + details: eventData, + metadata: metaData + } }); this.events.push({ ...partialEvent, diff --git a/src/event-cache/__tests__/EventCache.test.ts b/src/event-cache/__tests__/EventCache.test.ts index 501faf05..a6816bd7 100644 --- a/src/event-cache/__tests__/EventCache.test.ts +++ b/src/event-cache/__tests__/EventCache.test.ts @@ -519,19 +519,56 @@ describe('EventCache tests', () => { expect(bus.dispatch).toHaveBeenCalledWith( Topic.EVENTS, expect.objectContaining({ - id: expect.stringMatching(/[0-9a-f\-]+/), - timestamp: new Date(), - type: EVENT1_SCHEMA, - metadata: expect.objectContaining({ - version: '1.0.0', - 'aws:client': INSTALL_MODULE, - 'aws:clientVersion': WEB_CLIENT_VERSION - }), - details: expect.objectContaining({}) + payload: expect.objectContaining({ + id: expect.stringMatching(/[0-9a-f\-]+/), + timestamp: new Date(), + type: EVENT1_SCHEMA, + metadata: expect.objectContaining({ + version: '1.0.0', + 'aws:client': INSTALL_MODULE, + 'aws:clientVersion': WEB_CLIENT_VERSION + }), + details: expect.objectContaining({}) + }) }) ); }); + test('when event is recorded with key then the key is dispatched', async () => { + const EVENT1_SCHEMA = 'com.amazon.rum.event1'; + const bus = new EventBus(); + const eventCache: EventCache = Utils.createEventCache( + DEFAULT_CONFIG, + bus + ); + + // run + eventCache.recordEvent(EVENT1_SCHEMA, {}, 'key'); + + // eslint-disable-next-line + expect(bus.dispatch).toHaveBeenCalledWith( + Topic.EVENTS, + expect.objectContaining({ key: 'key' }) + ); + }); + + test('when event is recorded without key then dispatched message does not contain key', async () => { + const EVENT1_SCHEMA = 'com.amazon.rum.event1'; + const bus = new EventBus(); + const eventCache: EventCache = Utils.createEventCache( + DEFAULT_CONFIG, + bus + ); + + // run + eventCache.recordEvent(EVENT1_SCHEMA, {}); + // eslint-disable-next-line + expect(bus.dispatch).not.toHaveBeenCalledWith( + expect.anything(), + expect.objectContaining({ key: 'key' }) + ); + }); + test('when cache is disabled then subscribers are not notified', async () => { // Init const EVENT1_SCHEMA = 'com.amazon.rum.event1'; diff --git a/src/plugins/types.ts b/src/plugins/types.ts index 670b52de..c232d8cc 100644 --- a/src/plugins/types.ts +++ b/src/plugins/types.ts @@ -2,7 +2,7 @@ import { Config } from '../orchestration/Orchestration'; import { Session } from '../sessions/SessionManager'; import EventBus, { Topic } from '../event-bus/EventBus'; -export type RecordEvent = (type: string, eventData: object) => void; +export type RecordEvent = (type: string, eventData: object, key?: any) => void; export type RecordPageView = (pageId: string) => void; export type GetSession = () => Session | undefined; From 75483833e330f1d4b642d64be27a5b4903c10297 Mon Sep 17 00:00:00 2001 From: Billy Date: Fri, 8 Sep 2023 14:51:18 -0700 Subject: [PATCH 22/28] chore: dispatch resource events with keys --- src/plugins/event-plugins/ResourcePlugin.ts | 6 +++++- src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts | 3 +++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/plugins/event-plugins/ResourcePlugin.ts b/src/plugins/event-plugins/ResourcePlugin.ts index 6d0c35ac..6ec1bb56 100644 --- a/src/plugins/event-plugins/ResourcePlugin.ts +++ b/src/plugins/event-plugins/ResourcePlugin.ts @@ -127,7 +127,11 @@ export class ResourcePlugin extends InternalPlugin { if (this.context.config.recordResourceUrl) { eventData.targetUrl = entryData.name; } - this.context.record(PERFORMANCE_RESOURCE_EVENT_TYPE, eventData); + this.context.record( + PERFORMANCE_RESOURCE_EVENT_TYPE, + eventData, + entryData + ); } }; diff --git a/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts b/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts index 26fd8a91..7c71d7d5 100644 --- a/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts @@ -65,6 +65,9 @@ describe('ResourcePlugin tests', () => { initiatorType: resourceEvent.initiatorType }) ); + expect(record.mock.calls[0][2]).toEqual( + expect.objectContaining(resourceEvent) + ); }); test('when recordResourceUrl is false then the resource name is not recorded', async () => { From 8cda240e104144bd6cea6297382cf85cf49a0104 Mon Sep 17 00:00:00 2001 From: Billy Date: Fri, 8 Sep 2023 14:55:33 -0700 Subject: [PATCH 23/28] chore: dispatch lvl 2 navigation events with key --- src/plugins/event-plugins/NavigationPlugin.ts | 3 ++- .../event-plugins/__tests__/NavigationPlugin.test.ts | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/plugins/event-plugins/NavigationPlugin.ts b/src/plugins/event-plugins/NavigationPlugin.ts index 788ebe92..f7bb2a0d 100644 --- a/src/plugins/event-plugins/NavigationPlugin.ts +++ b/src/plugins/event-plugins/NavigationPlugin.ts @@ -265,7 +265,8 @@ export class NavigationPlugin extends InternalPlugin { if (this.context?.record) { this.context.record( PERFORMANCE_NAVIGATION_EVENT_TYPE, - eventDataNavigationTimingLevel2 + eventDataNavigationTimingLevel2, + entryData ); } }; diff --git a/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts b/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts index 6e40578e..ef41f0ce 100644 --- a/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts @@ -44,6 +44,10 @@ describe('NavigationPlugin tests', () => { navigationType: navigationEvent.type }) ); + // expect to record with key=PerformanceNavigationEntry + expect(record.mock.calls[0][2]).toEqual( + expect.objectContaining(navigationEvent) + ); }); test('When navigation timing level 2 API is not present then navigation timing level 1 API is recorded', async () => { @@ -77,6 +81,9 @@ describe('NavigationPlugin tests', () => { navigationTimingLevel: 1 }) ); + + // // expect to record without key + expect(record.mock.calls[0].length).toEqual(2); }); test('when enabled then events are recorded', async () => { From 0c0d73c7754802bee0894de8f59d949f350d106c Mon Sep 17 00:00:00 2001 From: Billy Date: Fri, 8 Sep 2023 16:09:40 -0700 Subject: [PATCH 24/28] nit: rename topic.events to event --- src/event-bus/EventBus.ts | 2 +- src/event-cache/EventCache.ts | 2 +- src/event-cache/__tests__/EventCache.test.ts | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/event-bus/EventBus.ts b/src/event-bus/EventBus.ts index caf6cf77..d9719217 100644 --- a/src/event-bus/EventBus.ts +++ b/src/event-bus/EventBus.ts @@ -4,7 +4,7 @@ export interface Message { payload: any; } export enum Topic { - EVENTS = 'events' + EVENT = 'event' } /** A topic-based event bus to facilitate communication between plugins */ diff --git a/src/event-cache/EventCache.ts b/src/event-cache/EventCache.ts index 3eb1e2c9..1e54d2d8 100644 --- a/src/event-cache/EventCache.ts +++ b/src/event-cache/EventCache.ts @@ -230,7 +230,7 @@ export class EventCache { timestamp: new Date(), type }; - this.eventBus.dispatch(Topic.EVENTS, { + this.eventBus.dispatch(Topic.EVENT, { ...(key && { key }), payload: { ...partialEvent, diff --git a/src/event-cache/__tests__/EventCache.test.ts b/src/event-cache/__tests__/EventCache.test.ts index a6816bd7..f432bbca 100644 --- a/src/event-cache/__tests__/EventCache.test.ts +++ b/src/event-cache/__tests__/EventCache.test.ts @@ -517,7 +517,7 @@ describe('EventCache tests', () => { expect(eventBatch).toEqual(expect.arrayContaining([event])); // eslint-disable-next-line expect(bus.dispatch).toHaveBeenCalledWith( - Topic.EVENTS, + Topic.EVENT, expect.objectContaining({ payload: expect.objectContaining({ id: expect.stringMatching(/[0-9a-f\-]+/), @@ -547,7 +547,7 @@ describe('EventCache tests', () => { // eslint-disable-next-line expect(bus.dispatch).toHaveBeenCalledWith( - Topic.EVENTS, + Topic.EVENT, expect.objectContaining({ key: 'key' }) ); }); From a4132cc6adce0bb00af863d2a2ee053033cc2b41 Mon Sep 17 00:00:00 2001 From: Billy Date: Sat, 9 Sep 2023 09:44:16 -0700 Subject: [PATCH 25/28] chore: fix merge conflict --- src/plugins/event-plugins/__tests__/FetchPlugin.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/plugins/event-plugins/__tests__/FetchPlugin.test.ts b/src/plugins/event-plugins/__tests__/FetchPlugin.test.ts index b30c688e..31cc68db 100644 --- a/src/plugins/event-plugins/__tests__/FetchPlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/FetchPlugin.test.ts @@ -7,6 +7,7 @@ import { advanceTo } from 'jest-date-mock'; import { context, DEFAULT_CONFIG, + getSession, record, recordPageView, xRayOffContext, From 8400db0b455083c4120e88929b8b49abfe7045c6 Mon Sep 17 00:00:00 2001 From: Billy Date: Sat, 9 Sep 2023 09:50:08 -0700 Subject: [PATCH 26/28] nit: remove unnecessary import --- src/orchestration/Orchestration.ts | 2 +- src/test-utils/test-utils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/orchestration/Orchestration.ts b/src/orchestration/Orchestration.ts index 14ed3641..4f363e41 100644 --- a/src/orchestration/Orchestration.ts +++ b/src/orchestration/Orchestration.ts @@ -207,7 +207,7 @@ export class Orchestration { private eventCache: EventCache; private dispatchManager: Dispatch; private config: Config; - private eventBus = new EventBus(); + private eventBus = new EventBus(); /** * Instantiate the CloudWatch RUM web client and begin monitoring the diff --git a/src/test-utils/test-utils.ts b/src/test-utils/test-utils.ts index 04e642da..592c0136 100644 --- a/src/test-utils/test-utils.ts +++ b/src/test-utils/test-utils.ts @@ -17,7 +17,7 @@ import { UserDetails } from '../dispatch/dataplane'; import { ReadableStream } from 'web-streams-polyfill'; -import EventBus, { Topic } from '../event-bus/EventBus'; +import EventBus from '../event-bus/EventBus'; jest.mock('../event-bus/EventBus'); export const AWS_RUM_ENDPOINT = new URL( From b17af20ace2b7430d3f7ca3f20eec653cc727e82 Mon Sep 17 00:00:00 2001 From: Billy Date: Sun, 10 Sep 2023 22:50:06 -0700 Subject: [PATCH 27/28] chore: remove key --- src/event-bus/EventBus.ts | 8 +--- src/event-bus/__tests__/EventBus.test.ts | 14 +++---- src/event-cache/EventCache.ts | 5 +-- src/event-cache/__tests__/EventCache.test.ts | 37 +------------------ src/plugins/event-plugins/NavigationPlugin.ts | 3 +- src/plugins/event-plugins/ResourcePlugin.ts | 6 +-- .../__tests__/NavigationPlugin.test.ts | 4 -- .../__tests__/ResourcePlugin.test.ts | 3 -- src/plugins/types.ts | 2 +- 9 files changed, 15 insertions(+), 67 deletions(-) diff --git a/src/event-bus/EventBus.ts b/src/event-bus/EventBus.ts index d9719217..0f0a0d3d 100644 --- a/src/event-bus/EventBus.ts +++ b/src/event-bus/EventBus.ts @@ -1,8 +1,4 @@ -export type Subscriber = (message: Message) => void; -export interface Message { - key?: any; - payload: any; -} +export type Subscriber = (message: any) => void; export enum Topic { EVENT = 'event' } @@ -33,7 +29,7 @@ export default class EventBus { return false; } - dispatch(topic: T, message: Message): void { + dispatch(topic: T, message: any): void { const list = this.subscribers.get(topic); if (list) { for (const subscriber of list) { diff --git a/src/event-bus/__tests__/EventBus.test.ts b/src/event-bus/__tests__/EventBus.test.ts index 63f6fb0c..e11ac2ac 100644 --- a/src/event-bus/__tests__/EventBus.test.ts +++ b/src/event-bus/__tests__/EventBus.test.ts @@ -1,4 +1,4 @@ -import EventBus, { Topic } from '../EventBus'; +import EventBus from '../EventBus'; export enum MockTopics { FOOD = 'food', @@ -18,11 +18,11 @@ describe('EventBus tests', () => { eventBus.subscribe(MockTopics.FOOD, l2); // run - eventBus.dispatch(MockTopics.FOOD, { payload: 'burger' }); + eventBus.dispatch(MockTopics.FOOD, 'burger'); // assert - expect(l1).toHaveBeenCalledWith({ payload: 'burger' }); - expect(l2).toHaveBeenCalledWith({ payload: 'burger' }); + expect(l1).toHaveBeenCalledWith('burger'); + expect(l2).toHaveBeenCalledWith('burger'); }); test('when subscriber is removed then it is not called', async () => { @@ -32,10 +32,10 @@ describe('EventBus tests', () => { const removed = eventBus.unsubscribe(MockTopics.FOOD, l2); // run - eventBus.dispatch(MockTopics.FOOD, { payload: 'burger' }); + eventBus.dispatch(MockTopics.FOOD, 'burger'); // assert - expect(l1).toHaveBeenCalledWith({ payload: 'burger' }); + expect(l1).toHaveBeenCalledWith('burger'); expect(removed).toBe(true); expect(l2).not.toHaveBeenCalled(); }); @@ -45,7 +45,7 @@ describe('EventBus tests', () => { eventBus.subscribe(MockTopics.BOOKS, l2); // run - eventBus.dispatch(MockTopics.FOOD, { payload: 'burger' }); + eventBus.dispatch(MockTopics.FOOD, 'burger'); // assert expect(l2).not.toHaveBeenCalled(); diff --git a/src/event-cache/EventCache.ts b/src/event-cache/EventCache.ts index 1e54d2d8..f674f8bf 100644 --- a/src/event-cache/EventCache.ts +++ b/src/event-cache/EventCache.ts @@ -88,7 +88,7 @@ export class EventCache { * * @param type The event schema. */ - public recordEvent = (type: string, eventData: object, key?: any) => { + public recordEvent = (type: string, eventData: object) => { if (!this.enabled) { return; } @@ -98,7 +98,7 @@ export class EventCache { this.sessionManager.incrementSessionEventCount(); if (this.canRecord(session)) { - this.addRecordToCache(type, eventData, key); + this.addRecordToCache(type, eventData); } } }; @@ -231,7 +231,6 @@ export class EventCache { type }; this.eventBus.dispatch(Topic.EVENT, { - ...(key && { key }), payload: { ...partialEvent, details: eventData, diff --git a/src/event-cache/__tests__/EventCache.test.ts b/src/event-cache/__tests__/EventCache.test.ts index f432bbca..b0b5e9b5 100644 --- a/src/event-cache/__tests__/EventCache.test.ts +++ b/src/event-cache/__tests__/EventCache.test.ts @@ -494,7 +494,7 @@ describe('EventCache tests', () => { expect(eventCache.getEventBatch().length).toEqual(1); }); - test('when event is recorded then events subscribers are notified with raw event', async () => { + test('when event is recorded then events subscribers are notified with parsed rum event', async () => { // Init const EVENT1_SCHEMA = 'com.amazon.rum.event1'; const bus = new EventBus(); @@ -534,41 +534,6 @@ describe('EventCache tests', () => { ); }); - test('when event is recorded with key then the key is dispatched', async () => { - const EVENT1_SCHEMA = 'com.amazon.rum.event1'; - const bus = new EventBus(); - const eventCache: EventCache = Utils.createEventCache( - DEFAULT_CONFIG, - bus - ); - - // run - eventCache.recordEvent(EVENT1_SCHEMA, {}, 'key'); - - // eslint-disable-next-line - expect(bus.dispatch).toHaveBeenCalledWith( - Topic.EVENT, - expect.objectContaining({ key: 'key' }) - ); - }); - - test('when event is recorded without key then dispatched message does not contain key', async () => { - const EVENT1_SCHEMA = 'com.amazon.rum.event1'; - const bus = new EventBus(); - const eventCache: EventCache = Utils.createEventCache( - DEFAULT_CONFIG, - bus - ); - - // run - eventCache.recordEvent(EVENT1_SCHEMA, {}); - // eslint-disable-next-line - expect(bus.dispatch).not.toHaveBeenCalledWith( - expect.anything(), - expect.objectContaining({ key: 'key' }) - ); - }); - test('when cache is disabled then subscribers are not notified', async () => { // Init const EVENT1_SCHEMA = 'com.amazon.rum.event1'; diff --git a/src/plugins/event-plugins/NavigationPlugin.ts b/src/plugins/event-plugins/NavigationPlugin.ts index f7bb2a0d..788ebe92 100644 --- a/src/plugins/event-plugins/NavigationPlugin.ts +++ b/src/plugins/event-plugins/NavigationPlugin.ts @@ -265,8 +265,7 @@ export class NavigationPlugin extends InternalPlugin { if (this.context?.record) { this.context.record( PERFORMANCE_NAVIGATION_EVENT_TYPE, - eventDataNavigationTimingLevel2, - entryData + eventDataNavigationTimingLevel2 ); } }; diff --git a/src/plugins/event-plugins/ResourcePlugin.ts b/src/plugins/event-plugins/ResourcePlugin.ts index 6ec1bb56..6d0c35ac 100644 --- a/src/plugins/event-plugins/ResourcePlugin.ts +++ b/src/plugins/event-plugins/ResourcePlugin.ts @@ -127,11 +127,7 @@ export class ResourcePlugin extends InternalPlugin { if (this.context.config.recordResourceUrl) { eventData.targetUrl = entryData.name; } - this.context.record( - PERFORMANCE_RESOURCE_EVENT_TYPE, - eventData, - entryData - ); + this.context.record(PERFORMANCE_RESOURCE_EVENT_TYPE, eventData); } }; diff --git a/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts b/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts index ef41f0ce..8eef4662 100644 --- a/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts @@ -44,10 +44,6 @@ describe('NavigationPlugin tests', () => { navigationType: navigationEvent.type }) ); - // expect to record with key=PerformanceNavigationEntry - expect(record.mock.calls[0][2]).toEqual( - expect.objectContaining(navigationEvent) - ); }); test('When navigation timing level 2 API is not present then navigation timing level 1 API is recorded', async () => { diff --git a/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts b/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts index 7c71d7d5..26fd8a91 100644 --- a/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/ResourcePlugin.test.ts @@ -65,9 +65,6 @@ describe('ResourcePlugin tests', () => { initiatorType: resourceEvent.initiatorType }) ); - expect(record.mock.calls[0][2]).toEqual( - expect.objectContaining(resourceEvent) - ); }); test('when recordResourceUrl is false then the resource name is not recorded', async () => { diff --git a/src/plugins/types.ts b/src/plugins/types.ts index c232d8cc..670b52de 100644 --- a/src/plugins/types.ts +++ b/src/plugins/types.ts @@ -2,7 +2,7 @@ import { Config } from '../orchestration/Orchestration'; import { Session } from '../sessions/SessionManager'; import EventBus, { Topic } from '../event-bus/EventBus'; -export type RecordEvent = (type: string, eventData: object, key?: any) => void; +export type RecordEvent = (type: string, eventData: object) => void; export type RecordPageView = (pageId: string) => void; export type GetSession = () => Session | undefined; From bf194e7995567d6e597fb21b3c26f771875725ea Mon Sep 17 00:00:00 2001 From: Billy Date: Mon, 11 Sep 2023 09:05:56 -0700 Subject: [PATCH 28/28] chore: remove remaining key usages --- src/event-cache/EventCache.ts | 10 ++++------ src/event-cache/__tests__/EventCache.test.ts | 20 +++++++++---------- .../__tests__/NavigationPlugin.test.ts | 3 --- 3 files changed, 13 insertions(+), 20 deletions(-) diff --git a/src/event-cache/EventCache.ts b/src/event-cache/EventCache.ts index f674f8bf..af8266bf 100644 --- a/src/event-cache/EventCache.ts +++ b/src/event-cache/EventCache.ts @@ -203,7 +203,7 @@ export class EventCache { * * @param type The event schema. */ - private addRecordToCache = (type: string, eventData: object, key?: any) => { + private addRecordToCache = (type: string, eventData: object) => { if (!this.enabled) { return; } @@ -231,11 +231,9 @@ export class EventCache { type }; this.eventBus.dispatch(Topic.EVENT, { - payload: { - ...partialEvent, - details: eventData, - metadata: metaData - } + ...partialEvent, + details: eventData, + metadata: metaData }); this.events.push({ ...partialEvent, diff --git a/src/event-cache/__tests__/EventCache.test.ts b/src/event-cache/__tests__/EventCache.test.ts index b0b5e9b5..ad3d9fef 100644 --- a/src/event-cache/__tests__/EventCache.test.ts +++ b/src/event-cache/__tests__/EventCache.test.ts @@ -519,17 +519,15 @@ describe('EventCache tests', () => { expect(bus.dispatch).toHaveBeenCalledWith( Topic.EVENT, expect.objectContaining({ - payload: expect.objectContaining({ - id: expect.stringMatching(/[0-9a-f\-]+/), - timestamp: new Date(), - type: EVENT1_SCHEMA, - metadata: expect.objectContaining({ - version: '1.0.0', - 'aws:client': INSTALL_MODULE, - 'aws:clientVersion': WEB_CLIENT_VERSION - }), - details: expect.objectContaining({}) - }) + id: expect.stringMatching(/[0-9a-f\-]+/), + timestamp: new Date(), + type: EVENT1_SCHEMA, + metadata: expect.objectContaining({ + version: '1.0.0', + 'aws:client': INSTALL_MODULE, + 'aws:clientVersion': WEB_CLIENT_VERSION + }), + details: expect.objectContaining({}) }) ); }); diff --git a/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts b/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts index 8eef4662..6e40578e 100644 --- a/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts +++ b/src/plugins/event-plugins/__tests__/NavigationPlugin.test.ts @@ -77,9 +77,6 @@ describe('NavigationPlugin tests', () => { navigationTimingLevel: 1 }) ); - - // // expect to record without key - expect(record.mock.calls[0].length).toEqual(2); }); test('when enabled then events are recorded', async () => {