Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
ref(replay): Extract addEvent out
  • Loading branch information
mydea committed Dec 9, 2022
commit 721e682282c7f0a429ccdff6df66018edacc60bc
53 changes: 10 additions & 43 deletions packages/replay/src/replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { createPayload } from './util/createPayload';
import { isExpired } from './util/isExpired';
import { isSessionExpired } from './util/isSessionExpired';
import { overwriteRecordDroppedEvent, restoreRecordDroppedEvent } from './util/monkeyPatchRecordDroppedEvent';
import { addEvent } from './util/addEvent';

/**
* Returns true to return control to calling function, otherwise continue with normal batching
Expand Down Expand Up @@ -137,6 +138,11 @@ export class ReplayContainer {
return this._isEnabled;
}

/** If recording is currently paused. */
public isPaused(): boolean {
return this._isPaused;
}

/**
* Initializes the plugin.
*
Expand Down Expand Up @@ -439,7 +445,7 @@ export class ReplayContainer {

// We need to clear existing events on a checkout, otherwise they are
// incremental event updates and should be appended
this.addEvent(event, isCheckout);
addEvent(this, event, isCheckout);

// Different behavior for full snapshots (type=2), ignore other event types
// See https://github.com/rrweb-io/rrweb/blob/d8f9290ca496712aa1e7d472549480c4e7876594/packages/rrweb/src/types.ts#L16
Expand Down Expand Up @@ -553,7 +559,7 @@ export class ReplayContainer {
}

this.addUpdate(() => {
this.addEvent({
addEvent(this, {
type: EventType.Custom,
// TODO: We were converting from ms to seconds for breadcrumbs, spans,
// but maybe we should just keep them as milliseconds
Expand Down Expand Up @@ -623,45 +629,6 @@ export class ReplayContainer {
record.takeFullSnapshot(true);
}

/**
* Add an event to the event buffer
*/
addEvent(event: RecordingEvent, isCheckout?: boolean): void {
if (!this.eventBuffer) {
// This implies that `_isEnabled` is false
return;
}

if (this._isPaused) {
// Do not add to event buffer when recording is paused
return;
}

// TODO: sadness -- we will want to normalize timestamps to be in ms -
// requires coordination with frontend
const isMs = event.timestamp > 9999999999;
const timestampInMs = isMs ? event.timestamp : event.timestamp * 1000;

// Throw out events that happen more than 5 minutes ago. This can happen if
// page has been left open and idle for a long period of time and user
// comes back to trigger a new session. The performance entries rely on
// `performance.timeOrigin`, which is when the page first opened.
if (timestampInMs + SESSION_IDLE_DURATION < new Date().getTime()) {
return;
}

// Only record earliest event if a new session was created, otherwise it
// shouldn't be relevant
if (
this.session?.segmentId === 0 &&
(!this._context.earliestEvent || timestampInMs < this._context.earliestEvent)
) {
this._context.earliestEvent = timestampInMs;
}

this.eventBuffer.addEvent(event, isCheckout);
}

/**
* Update user activity (across session lifespans)
*/
Expand Down Expand Up @@ -710,7 +677,7 @@ export class ReplayContainer {
*/
createCustomBreadcrumb(breadcrumb: Breadcrumb): void {
this.addUpdate(() => {
this.addEvent({
addEvent(this, {
type: EventType.Custom,
timestamp: breadcrumb.timestamp || 0,
data: {
Expand All @@ -727,7 +694,7 @@ export class ReplayContainer {
createPerformanceSpans(entries: ReplayPerformanceEntry[]): Promise<void[]> {
return Promise.all(
entries.map(({ type, start, end, name, data }) =>
this.addEvent({
addEvent(this, {
type: EventType.Custom,
timestamp: start,
data: {
Expand Down
40 changes: 40 additions & 0 deletions packages/replay/src/util/addEvent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { SESSION_IDLE_DURATION } from '../constants';
import { ReplayContainer } from '../replay';
import { RecordingEvent } from '../types';

/**
* Add an event to the event buffer
*/
export function addEvent(replay: ReplayContainer, event: RecordingEvent, isCheckout?: boolean): void {
if (!replay.eventBuffer) {
// This implies that `_isEnabled` is false
return;
}

if (replay.isPaused()) {
// Do not add to event buffer when recording is paused
return;
}

// TODO: sadness -- we will want to normalize timestamps to be in ms -
// requires coordination with frontend
const isMs = event.timestamp > 9999999999;
const timestampInMs = isMs ? event.timestamp : event.timestamp * 1000;

// Throw out events that happen more than 5 minutes ago. This can happen if
// page has been left open and idle for a long period of time and user
// comes back to trigger a new session. The performance entries rely on
// `performance.timeOrigin`, which is when the page first opened.
if (timestampInMs + SESSION_IDLE_DURATION < new Date().getTime()) {
return;
}

// Only record earliest event if a new session was created, otherwise it
// shouldn't be relevant
const earliestEvent = replay.getContext().earliestEvent;
if (replay.session?.segmentId === 0 && (!earliestEvent || timestampInMs < earliestEvent)) {
replay.getContext().earliestEvent = timestampInMs;
}

replay.eventBuffer.addEvent(event, isCheckout);
}
3 changes: 2 additions & 1 deletion packages/replay/test/unit/index-errorSampleRate.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ jest.unmock('@sentry/browser');
import { captureException } from '@sentry/browser';

import { REPLAY_SESSION_KEY, VISIBILITY_CHANGE_TIMEOUT, WINDOW } from '../../src/constants';
import { addEvent } from '../../src/util/addEvent';
import { ReplayContainer } from './../../src/replay';
import { PerformanceEntryResource } from './../fixtures/performanceEntry/resource';
import { BASE_TIMESTAMP, RecordMock } from './../index';
Expand Down Expand Up @@ -195,7 +196,7 @@ describe('Replay (errorSampleRate)', () => {
jest.advanceTimersByTime(ELAPSED);

const TEST_EVENT = { data: {}, timestamp: BASE_TIMESTAMP, type: 2 };
replay.addEvent(TEST_EVENT);
addEvent(replay, TEST_EVENT);

document.dispatchEvent(new Event('visibilitychange'));

Expand Down
3 changes: 2 additions & 1 deletion packages/replay/test/unit/index-noSticky.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Transport } from '@sentry/types';
import * as SentryUtils from '@sentry/utils';

import { SESSION_IDLE_DURATION, VISIBILITY_CHANGE_TIMEOUT } from '../../src/constants';
import { addEvent } from '../../src/util/addEvent';
import { ReplayContainer } from './../../src/replay';
import { BASE_TIMESTAMP, mockRrweb, mockSdk } from './../index';
import { useFakeTimers } from './../utils/use-fake-timers';
Expand Down Expand Up @@ -119,7 +120,7 @@ describe('Replay (no sticky)', () => {
jest.advanceTimersByTime(ELAPSED);

const TEST_EVENT = { data: {}, timestamp: BASE_TIMESTAMP, type: 2 };
replay.addEvent(TEST_EVENT);
addEvent(replay, TEST_EVENT);

document.dispatchEvent(new Event('visibilitychange'));

Expand Down
19 changes: 10 additions & 9 deletions packages/replay/test/unit/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { EventType } from 'rrweb';
import { MAX_SESSION_LIFE, REPLAY_SESSION_KEY, VISIBILITY_CHANGE_TIMEOUT, WINDOW } from '../../src/constants';
import { ReplayContainer } from '../../src/replay';
import { RecordingEvent } from '../../src/types';
import { addEvent } from '../../src/util/addEvent';
import { useFakeTimers } from '../utils/use-fake-timers';
import { PerformanceEntryResource } from './../fixtures/performanceEntry/resource';
import { BASE_TIMESTAMP, RecordMock } from './../index';
Expand Down Expand Up @@ -82,7 +83,7 @@ describe('Replay with custom mock', () => {
timestamp: new Date().valueOf(),
} as RecordingEvent;

replay.addEvent(event);
addEvent(replay, event);

await replay.runFlush();

Expand Down Expand Up @@ -250,7 +251,7 @@ describe('Replay', () => {
},
};

replay.addEvent(TEST_EVENT);
addEvent(replay, TEST_EVENT);
WINDOW.dispatchEvent(new Event('blur'));
await new Promise(process.nextTick);
expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled();
Expand All @@ -276,7 +277,7 @@ describe('Replay', () => {

const TEST_EVENT = { data: {}, timestamp: BASE_TIMESTAMP, type: 2 };

replay.addEvent(TEST_EVENT);
addEvent(replay, TEST_EVENT);
document.dispatchEvent(new Event('visibilitychange'));
jest.runAllTimers();
await new Promise(process.nextTick);
Expand Down Expand Up @@ -687,15 +688,15 @@ describe('Replay', () => {

const TEST_EVENT = { data: {}, timestamp: BASE_TIMESTAMP, type: 2 };

replay.addEvent(TEST_EVENT);
addEvent(replay, TEST_EVENT);
WINDOW.dispatchEvent(new Event('blur'));
await new Promise(process.nextTick);
expect(replay).toHaveSentReplay({
recordingPayloadHeader: { segment_id: 0 },
});
expect(replay.session?.segmentId).toBe(1);

replay.addEvent(TEST_EVENT);
addEvent(replay, TEST_EVENT);
WINDOW.dispatchEvent(new Event('blur'));
jest.runAllTimers();
await new Promise(process.nextTick);
Expand Down Expand Up @@ -727,7 +728,7 @@ describe('Replay', () => {
type: 2,
};

replay.addEvent(TEST_EVENT);
addEvent(replay, TEST_EVENT);
WINDOW.dispatchEvent(new Event('blur'));
await new Promise(process.nextTick);

Expand Down Expand Up @@ -812,10 +813,10 @@ describe('Replay', () => {
type: 2,
};

replay.addEvent(TEST_EVENT);
addEvent(replay, TEST_EVENT);

// Add a fake event that started BEFORE
replay.addEvent({
addEvent(replay, {
data: {},
timestamp: (BASE_TIMESTAMP - 10000) / 1000,
type: 5,
Expand Down Expand Up @@ -861,7 +862,7 @@ describe('Replay', () => {
type: 2,
};

replay.addEvent(TEST_EVENT);
addEvent(replay, TEST_EVENT);
// This event will trigger a flush
WINDOW.dispatchEvent(new Event('blur'));
jest.runAllTimers();
Expand Down
5 changes: 3 additions & 2 deletions packages/replay/test/unit/stop.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as SentryUtils from '@sentry/utils';

import { SESSION_IDLE_DURATION, WINDOW } from '../../src/constants';
import { ReplayContainer } from '../../src/replay';
import { addEvent } from '../../src/util/addEvent';
import { Replay } from './../../src';
// mock functions need to be imported first
import { BASE_TIMESTAMP, mockRrweb, mockSdk } from './../index';
Expand Down Expand Up @@ -73,7 +74,7 @@ describe('Replay - stop', () => {
// Pretend 5 seconds have passed
jest.advanceTimersByTime(ELAPSED);

replay.addEvent(TEST_EVENT);
addEvent(replay, TEST_EVENT);
WINDOW.dispatchEvent(new Event('blur'));
await new Promise(process.nextTick);
expect(mockRecord.takeFullSnapshot).not.toHaveBeenCalled();
Expand Down Expand Up @@ -103,7 +104,7 @@ describe('Replay - stop', () => {
},
};

replay.addEvent(TEST_EVENT);
addEvent(replay, TEST_EVENT);
WINDOW.dispatchEvent(new Event('blur'));
jest.runAllTimers();
await new Promise(process.nextTick);
Expand Down