Skip to content

fix(type): ✏️ breadcrumbs support timestamp as string #11119

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@ Before submitting a pull request, please take a look at our
[Contributing](https://github.com/getsentry/sentry-javascript/blob/master/CONTRIBUTING.md) guidelines and verify:

- [ ] If you've added code that should be tested, please add tests.
- [ ] Ensure your code lints and the test suite passes (`yarn lint`) & (`yarn test`).
- [ ] Ensure your code lints and the test suite passes (`yarn build && yarn lint && yarn test`).
17 changes: 11 additions & 6 deletions packages/replay-internal/src/coreHandlers/handleClick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ import { addBreadcrumbEvent } from './util/addBreadcrumbEvent';
import { getClosestInteractive } from './util/domUtils';
import { onWindowOpen } from './util/onWindowOpen';

type ClickBreadcrumb = Breadcrumb & {
timestamp: number;
};
type ClickBreadcrumb = Breadcrumb;

interface Click {
timestamp: number;
Expand Down Expand Up @@ -114,8 +112,12 @@ export class ClickDetector implements ReplayClickDetector {
return;
}

const timestampAsNumber = typeof breadcrumb.timestamp === 'string'
? new Date(breadcrumb.timestamp).getTime()
: breadcrumb.timestamp || 0;

const newClick: Click = {
timestamp: timestampToS(breadcrumb.timestamp),
timestamp: timestampToS(timestampAsNumber),
clickBreadcrumb: breadcrumb,
// Set this to 0 so we know it originates from the click breadcrumb
clickCount: 0,
Expand Down Expand Up @@ -209,6 +211,9 @@ export class ClickDetector implements ReplayClickDetector {

const isSlowClick = !hadScroll && !hadMutation;
const { clickCount, clickBreadcrumb } = click;
const timestampAsNumber = typeof clickBreadcrumb.timestamp === 'string'
? new Date(clickBreadcrumb.timestamp).getTime()
: clickBreadcrumb.timestamp || 0;

// Slow click
if (isSlowClick) {
Expand All @@ -220,7 +225,7 @@ export class ClickDetector implements ReplayClickDetector {
const breadcrumb: ReplaySlowClickFrame = {
type: 'default',
message: clickBreadcrumb.message,
timestamp: clickBreadcrumb.timestamp,
timestamp: timestampAsNumber,
category: 'ui.slowClickDetected',
data: {
...clickBreadcrumb.data,
Expand All @@ -243,7 +248,7 @@ export class ClickDetector implements ReplayClickDetector {
const breadcrumb: ReplayMultiClickFrame = {
type: 'default',
message: clickBreadcrumb.message,
timestamp: clickBreadcrumb.timestamp,
timestamp: timestampAsNumber,
category: 'ui.multiClick',
data: {
...clickBreadcrumb.data,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,17 @@ export function addBreadcrumbEvent(replay: ReplayContainer, breadcrumb: Breadcru
}

replay.addUpdate(() => {
const timestampAsNumber = typeof breadcrumb.timestamp === 'string'
? new Date(breadcrumb.timestamp).getTime() / 1000
: breadcrumb.timestamp || 0;

// This should never reject
// eslint-disable-next-line @typescript-eslint/no-floating-promises
replay.throttledAddEvent({
type: EventType.Custom,
// TODO: We were converting from ms to seconds for breadcrumbs, spans,
// but maybe we should just keep them as milliseconds
timestamp: (breadcrumb.timestamp || 0) * 1000,
timestamp: timestampAsNumber * 1000,
data: {
tag: 'breadcrumb',
// normalize to max. 10 depth and 1_000 properties per object
Expand Down
10 changes: 8 additions & 2 deletions packages/replay-internal/src/replay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -700,10 +700,13 @@ export class ReplayContainer implements ReplayContainerInterface {
});

this.addUpdate(() => {
const timestampAsNumber = typeof breadcrumb.timestamp === 'string'
? new Date(breadcrumb.timestamp).getTime()
: breadcrumb.timestamp || 0;
// Return `false` if the event _was_ added, as that means we schedule a flush
return !addEventSync(this, {
type: ReplayEventTypeCustom,
timestamp: breadcrumb.timestamp || 0,
timestamp: timestampAsNumber,
data: {
tag: 'breadcrumb',
payload: breadcrumb,
Expand Down Expand Up @@ -1003,11 +1006,14 @@ export class ReplayContainer implements ReplayContainerInterface {
*/
private _createCustomBreadcrumb(breadcrumb: ReplayBreadcrumbFrame): void {
this.addUpdate(() => {
const timestampAsNumber = typeof breadcrumb.timestamp === 'string'
? new Date(breadcrumb.timestamp).getTime()
: breadcrumb.timestamp || 0;
// This should never reject
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.throttledAddEvent({
type: EventType.Custom,
timestamp: breadcrumb.timestamp || 0,
timestamp: timestampAsNumber,
data: {
tag: 'breadcrumb',
payload: breadcrumb,
Expand Down
2 changes: 1 addition & 1 deletion packages/replay-internal/src/types/replayFrame.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type { ReplayEventTypeCustom } from './rrweb';
type AnyRecord = Record<string, any>; // eslint-disable-line @typescript-eslint/no-explicit-any

interface ReplayBaseBreadcrumbFrame {
timestamp: number;
timestamp: number | string;
/**
* For compatibility reasons
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,31 @@ describe('Unit | coreHandlers | util | addBreadcrumbEvent', function () {
},
]);
});

it('handles timestamp as string', async () => {
const baseTimestampAsISOString = new Date(BASE_TIMESTAMP).toISOString()
const breadcrumb: any = {
category: 'console',
message: 'Test message with timestamp as string',
timestamp: baseTimestampAsISOString,
};

const replay = setupReplayContainer();
addBreadcrumbEvent(replay, breadcrumb);

expect((replay.eventBuffer as EventBufferArray).events).toEqual([
{
type: 5,
timestamp: BASE_TIMESTAMP,
data: {
tag: 'breadcrumb',
payload: {
category: 'console',
message: 'Test message with timestamp as string',
timestamp: baseTimestampAsISOString,
},
},
},
]);
});
});