mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-09-29 22:34:33 +00:00
101 lines
4.0 KiB
TypeScript
101 lines
4.0 KiB
TypeScript
import invariant from 'invariant';
|
|
import {duration} from 'moment';
|
|
|
|
import isValidDate from 'sentry/utils/date/isValidDate';
|
|
import getMinMax from 'sentry/utils/getMinMax';
|
|
import type {ReplayRecord} from 'sentry/views/replays/types';
|
|
|
|
export function mapResponseToReplayRecord(apiResponse: any): ReplayRecord {
|
|
// Marshal special fields into tags
|
|
const user = Object.fromEntries(
|
|
Object.entries(apiResponse.user)
|
|
.filter(([key, value]) => key !== 'display_name' && value)
|
|
.map(([key, value]) => [`user.${key}`, [value]])
|
|
);
|
|
const unorderedTags: ReplayRecord['tags'] = {
|
|
...apiResponse.tags,
|
|
...(apiResponse.browser?.name ? {'browser.name': [apiResponse.browser.name]} : {}),
|
|
...(apiResponse.browser?.version
|
|
? {'browser.version': [apiResponse.browser.version]}
|
|
: {}),
|
|
...(apiResponse.device?.brand ? {'device.brand': [apiResponse.device.brand]} : {}),
|
|
...(apiResponse.device?.family ? {'device.family': [apiResponse.device.family]} : {}),
|
|
...(apiResponse.device?.model_id
|
|
? {'device.model_id': [apiResponse.device.model_id]}
|
|
: {}),
|
|
...(apiResponse.device?.name ? {'device.name': [apiResponse.device.name]} : {}),
|
|
...(apiResponse.environment ? {environment: [apiResponse.environment]} : {}),
|
|
...(apiResponse.platform ? {platform: [apiResponse.platform]} : {}),
|
|
...(apiResponse.releases ? {releases: [...apiResponse.releases]} : {}),
|
|
...(apiResponse.replay_type ? {replayType: [apiResponse.replay_type]} : {}),
|
|
...(apiResponse.os?.name ? {'os.name': [apiResponse.os.name]} : {}),
|
|
...(apiResponse.os?.version ? {'os.version': [apiResponse.os.version]} : {}),
|
|
...(apiResponse.sdk?.name ? {'sdk.name': [apiResponse.sdk.name]} : {}),
|
|
...(apiResponse.sdk?.version ? {'sdk.version': [apiResponse.sdk.version]} : {}),
|
|
...user,
|
|
};
|
|
|
|
const startedAt = new Date(apiResponse.started_at);
|
|
invariant(isValidDate(startedAt), 'replay.started_at is invalid');
|
|
const finishedAt = new Date(apiResponse.finished_at);
|
|
invariant(isValidDate(finishedAt), 'replay.finished_at is invalid');
|
|
return {
|
|
...apiResponse,
|
|
...(apiResponse.started_at ? {started_at: startedAt} : {}),
|
|
...(apiResponse.finished_at ? {finished_at: finishedAt} : {}),
|
|
...(apiResponse.duration !== undefined
|
|
? {duration: duration(apiResponse.duration * 1000)}
|
|
: {}),
|
|
tags: unorderedTags,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* We need to figure out the real start and end timestamps based on when
|
|
* first and last bits of data were collected. In milliseconds.
|
|
*
|
|
* @deprecated Once the backend returns the corrected timestamps, this is not needed.
|
|
*/
|
|
export function replayTimestamps(
|
|
replayRecord: ReplayRecord,
|
|
rrwebEvents: {timestamp: number}[],
|
|
rawCrumbs: {timestamp: number}[],
|
|
rawSpanData: {endTimestamp: number; op: string; startTimestamp: number}[]
|
|
) {
|
|
const rrwebTimestamps = rrwebEvents.map(event => event.timestamp).filter(Boolean);
|
|
const breadcrumbTimestamps = rawCrumbs
|
|
.map(rawCrumb => rawCrumb.timestamp)
|
|
.filter(Boolean);
|
|
const rawSpanDataFiltered = rawSpanData.filter(
|
|
({op}) => op !== 'largest-contentful-paint'
|
|
);
|
|
const spanStartTimestamps = rawSpanDataFiltered
|
|
.map(span => span.startTimestamp)
|
|
.filter(Boolean);
|
|
const spanEndTimestamps = rawSpanDataFiltered
|
|
.map(span => span.endTimestamp)
|
|
.filter(Boolean);
|
|
|
|
// Calculate min/max of each array individually, to prevent extra allocations.
|
|
// Also using `getMinMax()` so we can handle any huge arrays.
|
|
const {min: minRRWeb, max: maxRRWeb} = getMinMax(rrwebTimestamps);
|
|
const {min: minCrumbs, max: maxCrumbs} = getMinMax(breadcrumbTimestamps);
|
|
const {min: minSpanStarts} = getMinMax(spanStartTimestamps);
|
|
const {max: maxSpanEnds} = getMinMax(spanEndTimestamps);
|
|
|
|
return {
|
|
startTimestampMs: Math.min(
|
|
replayRecord.started_at.getTime(),
|
|
minRRWeb,
|
|
minCrumbs * 1000,
|
|
minSpanStarts * 1000
|
|
),
|
|
endTimestampMs: Math.max(
|
|
replayRecord.finished_at.getTime(),
|
|
maxRRWeb,
|
|
maxCrumbs * 1000,
|
|
maxSpanEnds * 1000
|
|
),
|
|
};
|
|
}
|