Skip to content

Commit

Permalink
refactor out integrations abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
billyvg committed Jan 10, 2024
1 parent 6277cc1 commit 3e11d66
Show file tree
Hide file tree
Showing 9 changed files with 54 additions and 39 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { expect } from '@playwright/test';
import type { ReplayCanvasIntegrationOptions } from '@sentry-internal/replay-canvas';

import { sentryTest } from '../../../../utils/fixtures';
import { getReplaySnapshot, shouldSkipReplayTest } from '../../../../utils/replayHelpers';
Expand All @@ -22,8 +21,8 @@ sentryTest('sets up canvas when adding ReplayCanvas integration first', async ({
await page.goto(url);

const replay = await getReplaySnapshot(page);
const canvasOptions = replay._integrations.canvas as ReplayCanvasIntegrationOptions;
expect(canvasOptions.sampling.canvas).toBe(2);
expect(canvasOptions.dataURLOptions.quality).toBe(0.4);
const canvasOptions = replay._canvas;
expect(canvasOptions?.sampling.canvas).toBe(2);
expect(canvasOptions?.dataURLOptions.quality).toBe(0.4);
expect(replay._hasCanvas).toBe(true);
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { expect } from '@playwright/test';
import type { ReplayCanvasIntegrationOptions } from '@sentry-internal/replay-canvas';

import { sentryTest } from '../../../../utils/fixtures';
import { getReplaySnapshot, shouldSkipReplayTest } from '../../../../utils/replayHelpers';
Expand All @@ -22,8 +21,8 @@ sentryTest('sets up canvas when adding ReplayCanvas integration after Replay', a
await page.goto(url);

const replay = await getReplaySnapshot(page);
const canvasOptions = replay._integrations.canvas as ReplayCanvasIntegrationOptions;
expect(canvasOptions.sampling.canvas).toBe(2);
expect(canvasOptions.dataURLOptions.quality).toBe(0.4);
const canvasOptions = replay._canvas;
expect(canvasOptions?.sampling.canvas).toBe(2);
expect(canvasOptions?.dataURLOptions.quality).toBe(0.4);
expect(replay._hasCanvas).toBe(true);
});
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ sentryTest('does not setup up canvas without ReplayCanvas integration', async ({
await page.goto(url);

const replay = await getReplaySnapshot(page);
const canvasOptions = replay._integrations.canvas;
const canvasOptions = replay._canvas;
expect(canvasOptions).toBe(undefined);
expect(replay._hasCanvas).toBe(false);
});
13 changes: 9 additions & 4 deletions dev-packages/browser-integration-tests/utils/replayHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* eslint-disable max-lines */
import { ReplayCanvasIntegrationOptions } from '@sentry-internal/replay-canvas';
import type { fullSnapshotEvent, incrementalSnapshotEvent } from '@sentry-internal/rrweb';
import { EventType } from '@sentry-internal/rrweb';
import type { ReplayEventWithTime } from '@sentry/browser';
Expand Down Expand Up @@ -174,23 +175,27 @@ export function getReplaySnapshot(page: Page): Promise<{
_isEnabled: boolean;
_context: InternalEventContext;
_options: ReplayPluginOptions;
_integrations: Record<string, unknown>;
_canvas: ReplayCanvasIntegrationOptions | undefined;
_hasCanvas: boolean;
session: Session | undefined;
recordingMode: ReplayRecordingMode;
}> {
return page.evaluate(() => {
const replayIntegration = (window as unknown as Window & { Replay: { _replay: ReplayContainer } }).Replay;
const replayIntegration = (
window as unknown as Window & {
Replay: { _replay: ReplayContainer & { _canvas: ReplayCanvasIntegrationOptions | undefined } };
}
).Replay;
const replay = replayIntegration._replay;

const replaySnapshot = {
_isPaused: replay.isPaused(),
_isEnabled: replay.isEnabled(),
_context: replay.getContext(),
_options: replay.getOptions(),
_integrations: replay.getIntegrations(),
_canvas: replay['_canvas'],
// We cannot pass the function through as this is serialized
_hasCanvas: typeof replay.getIntegrations().canvas?.getCanvasManager === 'function',
_hasCanvas: typeof replay['_canvas']?.getCanvasManager === 'function',
session: replay.session,
recordingMode: replay.recordingMode,
};
Expand Down
6 changes: 3 additions & 3 deletions packages/replay-canvas/src/canvas.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { CanvasManager } from '@sentry-internal/rrweb';
import { convertIntegrationFnToClass } from '@sentry/core';
import type { CanvasManagerInterface } from '@sentry/replay';
import { IntegrationFn } from '@sentry/types';
import type { IntegrationFn } from '@sentry/types';

interface ReplayCanvasOptions {
quality: 'low' | 'medium' | 'high';
Expand Down Expand Up @@ -69,8 +69,8 @@ const replayCanvasIntegration = ((options: Partial<ReplayCanvasOptions> = {}) =>
getCanvasManager: (options: ConstructorParameters<typeof CanvasManager>[0]) => new CanvasManager(options),
...(CANVAS_QUALITY[quality || 'medium'] || CANVAS_QUALITY.medium),
};
}
},
};
}) satisfies IntegrationFn;

export const ReplayCanvasIntegration = convertIntegrationFnToClass(replayCanvasIntegration, INTEGRATION_NAME);
export const ReplayCanvas = convertIntegrationFnToClass(INTEGRATION_NAME, replayCanvasIntegration);
12 changes: 9 additions & 3 deletions packages/replay/src/integration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import {
MIN_REPLAY_DURATION_LIMIT,
} from './constants';
import { ReplayContainer } from './replay';
import type { RecordingOptions, ReplayConfiguration, ReplayPluginOptions, SendBufferedReplayOptions } from './types';
import type {
RecordingOptions,
ReplayCanvasIntegrationOptions,
ReplayConfiguration,
ReplayPluginOptions,
SendBufferedReplayOptions,
} from './types';
import { getPrivacyOptions } from './util/getPrivacyOptions';
import { maskAttribute } from './util/maskAttribute';

Expand Down Expand Up @@ -355,13 +361,13 @@ Sentry.init({ replaysOnErrorSampleRate: ${errorSampleRate} })`,
try {
const client = getClient()!;
const canvasIntegration = client.getIntegrationById!('ReplayCanvas') as Integration & {
getOptions(): Partial<ReplayConfiguration>;
getOptions(): ReplayCanvasIntegrationOptions;
};
if (!canvasIntegration) {
return;
}

this._replay!.addIntegration('canvas', canvasIntegration.getOptions());
this._replay!['_canvas'] = canvasIntegration.getOptions();
} catch {
// ignore errors here
}
Expand Down
29 changes: 12 additions & 17 deletions packages/replay/src/replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import type {
RecordingEvent,
RecordingOptions,
ReplayBreadcrumbFrame,
ReplayCanvasIntegrationOptions,
ReplayContainer as ReplayContainerInterface,
ReplayPerformanceEntry,
ReplayPluginOptions,
Expand Down Expand Up @@ -145,9 +146,9 @@ export class ReplayContainer implements ReplayContainerInterface {
private _context: InternalEventContext;

/**
* Internal integrations (e.g. canvas)
* Internal use for canvas recording options
*/
private _integrations: Record<string, Record<string, unknown>>;
private _canvas: ReplayCanvasIntegrationOptions | undefined;

public constructor({
options,
Expand All @@ -167,7 +168,6 @@ export class ReplayContainer implements ReplayContainerInterface {
this._lastActivity = Date.now();
this._isEnabled = false;
this._isPaused = false;
this._integrations = {};
this._hasInitializedCoreListeners = false;
this._context = {
errorIds: new Set(),
Expand Down Expand Up @@ -228,11 +228,6 @@ export class ReplayContainer implements ReplayContainerInterface {
return this._options;
}

/** Get the replay integrations. [test only] */
public getIntegrations(): Record<string, Record<string, unknown>> {
return { ...this._integrations };
}

/**
* Initializes the plugin based on sampling configuration. Should not be
* called outside of constructor.
Expand Down Expand Up @@ -348,7 +343,7 @@ export class ReplayContainer implements ReplayContainerInterface {
*/
public startRecording(): void {
try {
const { canvas } = this._integrations;
const canvasOptions = this._canvas;

this._stopRecording = record({
...this._recordingOptions,
Expand All @@ -358,7 +353,14 @@ export class ReplayContainer implements ReplayContainerInterface {
...(this.recordingMode === 'buffer' && { checkoutEveryNms: BUFFER_CHECKOUT_TIME }),
emit: getHandleRecordingEmit(this),
onMutation: this._onMutationHandler,
...canvas,
...(canvasOptions
? {
recordCanvas: canvasOptions.recordCanvas,
getCanvasManager: canvasOptions.getCanvasManager,
sampling: canvasOptions.sampling,
dataURLOptions: canvasOptions.dataURLOptions,
}
: {}),
});
} catch (err) {
this._handleException(err);
Expand Down Expand Up @@ -723,13 +725,6 @@ export class ReplayContainer implements ReplayContainerInterface {
return spanToJSON(lastTransaction).description;
}

/**
* Internal integration use only, should not be public
*/
public addIntegration(name: string, options: Record<string, unknown>): void {
this._integrations[name] = options;
}

/**
* Initialize and start all listeners to varying events (DOM,
* Performance Observer, Recording, Sentry SDK, etc)
Expand Down
15 changes: 13 additions & 2 deletions packages/replay/src/types/replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type { SKIPPED, THROTTLED } from '../util/throttle';
import type { AllPerformanceEntry, AllPerformanceEntryData, ReplayPerformanceEntry } from './performance';
import type { ReplayFrameEvent } from './replayFrame';
import type { ReplayNetworkRequestOrResponse } from './request';
import type { ReplayEventWithTime, RrwebRecordOptions } from './rrweb';
import type { CanvasManagerInterface, CanvasManagerOptions, ReplayEventWithTime, RrwebRecordOptions } from './rrweb';

export type RecordingEvent = ReplayFrameEvent | ReplayEventWithTime;
export type RecordingOptions = RrwebRecordOptions;
Expand Down Expand Up @@ -502,7 +502,6 @@ export interface ReplayContainer {
updateUserActivity(): void;
addUpdate(cb: AddUpdateCallback): void;
getOptions(): ReplayPluginOptions;
getIntegrations(): Record<string, Record<string, unknown>>;
getSessionId(): string | undefined;
checkAndHandleExpiredSession(): boolean | void;
setInitialState(): void;
Expand Down Expand Up @@ -536,3 +535,15 @@ export interface SlowClickConfig {
scrollTimeout: number;
ignoreSelector: string;
}

export interface ReplayCanvasIntegrationOptions {
recordCanvas: true;
getCanvasManager: (options: CanvasManagerOptions) => CanvasManagerInterface;
sampling: {
canvas: number;
};
dataURLOptions: {
type: string;
quality: number;
};
}
2 changes: 1 addition & 1 deletion packages/replay/src/types/rrweb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export interface CanvasManagerInterface {
unlock(): void;
}

export interface GetCanvasManagerOptions {
export interface CanvasManagerOptions {
recordCanvas: boolean;
blockClass: string | RegExp;
blockSelector: string | null;
Expand Down

0 comments on commit 3e11d66

Please sign in to comment.