Skip to content

Commit cb9cb13

Browse files
authored
feat: Page tagging (#114)
1 parent d64a07c commit cb9cb13

File tree

11 files changed

+173
-13
lines changed

11 files changed

+173
-13
lines changed

app/page_event.html

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,13 @@
7171
cwr('recordPageView', '/page_view_two');
7272
}
7373

74+
function recordPageViewWithPageAttributes() {
75+
cwr('recordPageView', {
76+
pageId: '/page_view_two',
77+
pageTags: ['pageGroup1']
78+
});
79+
}
80+
7481
const parseEvents = () => {
7582
const requestBody = document.getElementById('request_body');
7683
const events = JSON.parse(requestBody.innerText).batch.events;
@@ -145,6 +152,12 @@
145152
<button id="recordPageView" onclick="recordPageView()">
146153
Record Page View
147154
</button>
155+
<button
156+
id="recordPageViewWithPageAttributes"
157+
onclick="recordPageViewWithPageAttributes()"
158+
>
159+
Record Page View with page attributes
160+
</button>
148161
<button id="doNotRecordPageView" onclick="doNotRecordPageView()">
149162
Do Not Record Page View
150163
</button>

src/CommandQueue.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export class CommandQueue {
3838
): void => {
3939
this.orchestration.setAwsCredentials(payload);
4040
},
41-
recordPageView: (payload: string): void => {
41+
recordPageView: (payload: any): void => {
4242
this.orchestration.recordPageView(payload);
4343
},
4444
recordError: (payload: any): void => {

src/__tests__/CommandQueue.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -259,7 +259,7 @@ describe('CommandQueue tests', () => {
259259
const cq: CommandQueue = getCommandQueue();
260260
await cq.push({
261261
c: 'recordPageView',
262-
p: 'page1'
262+
p: '/console/home'
263263
});
264264
expect(Orchestration).toHaveBeenCalled();
265265
expect(recordPageView).toHaveBeenCalled();

src/event-cache/EventCache.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Session, SessionManager } from '../sessions/SessionManager';
22
import { v4 } from 'uuid';
33
import { MetaData } from '../events/meta-data';
44
import { Config } from '../orchestration/Orchestration';
5-
import { PageManager } from '../sessions/PageManager';
5+
import { PageAttributes, PageManager } from '../sessions/PageManager';
66
import {
77
AppMonitorDetails,
88
UserDetails,
@@ -65,9 +65,9 @@ export class EventCache {
6565
/**
6666
* Update the current page interaction for the session.
6767
*/
68-
public recordPageView = (pageId: string) => {
68+
public recordPageView = (payload: string | PageAttributes) => {
6969
if (this.isCurrentUrlAllowed()) {
70-
this.pageManager.recordPageView(pageId);
70+
this.pageManager.recordPageView(payload);
7171
}
7272
};
7373

src/event-cache/__tests__/EventCache.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,35 @@ describe('EventCache tests', () => {
226226
);
227227
});
228228

229+
test('when page is recorded with page tags provided, event metadata records the page tag data', async () => {
230+
// Init
231+
const EVENT1_SCHEMA = 'com.amazon.rum.page_view_event';
232+
const eventCache: EventCache = Utils.createEventCache({
233+
...DEFAULT_CONFIG
234+
});
235+
const expectedEvents: RumEvent[] = [
236+
{
237+
id: expect.stringMatching(/[0-9a-f\-]+/),
238+
timestamp: new Date(),
239+
type: EVENT1_SCHEMA,
240+
metadata:
241+
'{"version":"1.0.0","title":"","pageId":"/rum/home","pageTags":["pageGroup1"]}',
242+
details: '{"version":"1.0.0","pageId":"/rum/home"}'
243+
}
244+
];
245+
246+
// Run
247+
eventCache.recordPageView({
248+
pageId: '/rum/home',
249+
pageTags: ['pageGroup1']
250+
});
251+
252+
// Assert
253+
expect(await eventCache.getEventBatch()).toEqual(
254+
expect.arrayContaining(expectedEvents)
255+
);
256+
});
257+
229258
test('when page matches both allowed and denied, recordEvent does not record the event', async () => {
230259
// Init
231260
const EVENT1_SCHEMA = 'com.amazon.rum.event1';

src/event-schemas/meta-data.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@
6262
},
6363
"domain": {
6464
"type": "string"
65-
}
65+
},
66+
"pageTags": { "type": "array", "items": { "type": "string" } }
6667
},
6768
"additionalProperties": false,
6869
"required": ["version", "domain"]

src/orchestration/Orchestration.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import { WebVitalsPlugin } from '../plugins/event-plugins/WebVitalsPlugin';
2020
import { XhrPlugin } from '../plugins/event-plugins/XhrPlugin';
2121
import { FetchPlugin } from '../plugins/event-plugins/FetchPlugin';
2222
import { PageViewPlugin } from '../plugins/event-plugins/PageViewPlugin';
23+
import { PageAttributes } from '../sessions/PageManager';
2324

2425
const DATA_PLANE_REGION_PLACEHOLDER = '${REGION}';
2526
const DATA_PLANE_DEFAULT_ENDPOINT =
@@ -284,10 +285,12 @@ export class Orchestration {
284285

285286
/**
286287
* Update the current page the user is interacting with.
287-
* @param pageId The unique ID for the page within the application.
288+
* @param payload Can be string or PageAttributes object
289+
* If string, payload is pageId (The unique ID for the page within the application).
290+
* If PageAttributes, payload contains pageId as well as page attributes to include in events with pageId
288291
*/
289-
public recordPageView(pageId: string) {
290-
this.eventCache.recordPageView(pageId);
292+
public recordPageView(payload: string | PageAttributes) {
293+
this.eventCache.recordPageView(payload);
291294
}
292295

293296
/**

src/orchestration/__tests__/Orchestration.test.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ jest.mock('../../dispatch/Dispatch', () => ({
1919

2020
const enableEventCache = jest.fn();
2121
const disableEventCache = jest.fn();
22+
const recordPageView = jest.fn();
2223

2324
jest.mock('../../event-cache/EventCache', () => ({
2425
EventCache: jest.fn().mockImplementation(() => ({
2526
enable: enableEventCache,
26-
disable: disableEventCache
27+
disable: disableEventCache,
28+
recordPageView: recordPageView
2729
}))
2830
}));
2931

@@ -353,4 +355,23 @@ describe('Orchestration tests', () => {
353355

354356
expect(actual).toEqual(expected);
355357
});
358+
359+
test('when the page is manually recorded with pageTag attribute then EventCache.recordPageView() is called', async () => {
360+
// Init
361+
const orchestration = new Orchestration('a', 'c', 'us-east-1', {});
362+
363+
const expected = {
364+
pageId: '/rum/home',
365+
pageTags: ['pageGroup1']
366+
};
367+
orchestration.recordPageView(expected);
368+
369+
let actual;
370+
371+
// Assert
372+
expect(recordPageView).toHaveBeenCalledTimes(1);
373+
actual = recordPageView.mock.calls[0][0];
374+
375+
expect(actual).toEqual(expected);
376+
});
356377
});

src/sessions/PageManager.ts

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ export type Attributes = {
1616
pageId: string;
1717
parentPageId?: string;
1818
interaction?: number;
19+
pageTags?: string[];
20+
};
21+
22+
export type PageAttributes = {
23+
pageId: string;
24+
pageTags?: string[];
1925
};
2026

2127
/**
@@ -68,7 +74,14 @@ export class PageManager {
6874
};
6975
}
7076

71-
public recordPageView(pageId: string) {
77+
public recordPageView(payload: string | PageAttributes) {
78+
let pageId;
79+
if (typeof payload === 'string') {
80+
pageId = payload;
81+
} else {
82+
pageId = payload.pageId;
83+
}
84+
7285
if (this.useCookies()) {
7386
this.recordInteraction = true;
7487
}
@@ -85,7 +98,9 @@ export class PageManager {
8598
}
8699

87100
// Attributes will be added to all events as meta data
88-
this.collectAttributes();
101+
this.collectAttributes(
102+
typeof payload === 'object' ? payload : undefined
103+
);
89104

90105
// The SessionManager will update its cookie with the new page
91106
this.recordPageViewEvent();
@@ -118,7 +133,7 @@ export class PageManager {
118133
};
119134
}
120135

121-
private collectAttributes() {
136+
private collectAttributes(customPageAttributes?: PageAttributes) {
122137
this.attributes = {
123138
title: document.title,
124139
pageId: this.page.pageId
@@ -130,6 +145,12 @@ export class PageManager {
130145
this.attributes.parentPageId = this.page.parentPageId;
131146
}
132147
}
148+
149+
if (customPageAttributes) {
150+
Object.keys(customPageAttributes).forEach((attribute) => {
151+
this.attributes[attribute] = customPageAttributes[attribute];
152+
});
153+
}
133154
}
134155

135156
private createPageViewEvent() {

src/sessions/__integ__/PageManager.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import { Selector } from 'testcafe';
22
import { REQUEST_BODY } from '../../test-utils/integ-test-utils';
33

44
const recordPageView: Selector = Selector(`#recordPageView`);
5+
const recordPageViewWithPageAttributes: Selector = Selector(
6+
`#recordPageViewWithPageAttributes`
7+
);
58
const dispatch: Selector = Selector(`#dispatch`);
69
const clear: Selector = Selector(`#clearRequestResponse`);
710
const doNotRecordPageView = Selector(`#doNotRecordPageView`);
@@ -115,3 +118,44 @@ test('when page is denied then page view is not recorded', async (t: TestControl
115118
pageInteractionId: '/page_view_two-1'
116119
});
117120
});
121+
122+
test('when pageTag attribute is passed in when manually recording page view event, then PageViewEventPlugin records pageTag data in metadata', async (t: TestController) => {
123+
// If we click too soon, the client/event collector plugin will not be loaded and will not record the click.
124+
// This could be a symptom of an issue with RUM web client load speed, or prioritization of script execution.
125+
126+
await t
127+
.wait(300)
128+
.click(dispatch)
129+
.expect(REQUEST_BODY.textContent)
130+
.contains('BatchId')
131+
.click(clear)
132+
.click(recordPageViewWithPageAttributes)
133+
.click(dispatch)
134+
.expect(REQUEST_BODY.textContent)
135+
.contains('BatchId');
136+
137+
const json = removeUnwantedEvents(
138+
JSON.parse(await REQUEST_BODY.textContent)
139+
);
140+
const eventType = json.RumEvents[0].type;
141+
const eventDetails = JSON.parse(json.RumEvents[0].details);
142+
const metaData = JSON.parse(json.RumEvents[0].metadata);
143+
144+
await t
145+
.expect(eventType)
146+
.eql('com.amazon.rum.page_view_event')
147+
.expect(eventDetails)
148+
.contains({
149+
pageId: '/page_view_two',
150+
interaction: 1,
151+
pageInteractionId: '/page_view_two-1',
152+
parentPageInteractionId: '/page_event.html-0'
153+
})
154+
.expect(metaData)
155+
.contains({
156+
pageId: '/page_view_two',
157+
title: 'RUM Integ Test'
158+
})
159+
.expect(metaData.pageTags[0])
160+
.eql('pageGroup1');
161+
});

0 commit comments

Comments
 (0)