mirror of
https://github.com/PostHog/posthog.git
synced 2024-11-24 00:47:50 +01:00
fix: patch mobile recordings that are missing their meta event (#24840)
This commit is contained in:
parent
a17ca6f070
commit
9a4e9a81da
@ -0,0 +1,6 @@
|
||||
export const encodedWebSnapshotData: string[] = [
|
||||
// first item could be a network event or something else
|
||||
'{"windowId":"0191C63B-03FF-73B5-96BE-40BE2761621C","data":{"payload":{"requests":[{"duration":28,"entryType":"resource","initiatorType":"fetch","method":"GET","name":"https://1.bp.blogspot.com/-hkNkoCjc5UA/T4JTlCjhhfI/AAAAAAAAB98/XxQwZ-QPkI8/s1600/Free+Google+Wallpapers+3.jpg","responseStatus":200,"timestamp":1725369200216,"transferSize":82375}]},"plugin":"rrweb/network@1"},"timestamp":1725369200216,"type":6,"seen":8833798676917222}',
|
||||
'{"windowId":"0191C63B-03FF-73B5-96BE-40BE2761621C","data":{"height":852,"width":393},"timestamp":1725607643113,"type":4,"seen":4930607506458337}',
|
||||
'{"windowId":"0191C63B-03FF-73B5-96BE-40BE2761621C","data":{"initialOffset":{"left":0,"top":0},"wireframes":[{"base64":"data:image/jpeg;base64,/9j/4AAQSkZJR","height":852,"id":4324378400,"type":"screenshot","width":393,"x":0,"y":0}]},"timestamp":1725607643113,"type":2,"seen":2118469619185818}',
|
||||
]
|
339
ee/frontend/mobile-replay/__snapshots__/parsing.test.ts.snap
Normal file
339
ee/frontend/mobile-replay/__snapshots__/parsing.test.ts.snap
Normal file
@ -0,0 +1,339 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`snapshot parsing handles mobile data with no meta event 1`] = `
|
||||
[
|
||||
{
|
||||
"data": {
|
||||
"payload": {
|
||||
"requests": [
|
||||
{
|
||||
"duration": 28,
|
||||
"entryType": "resource",
|
||||
"initiatorType": "fetch",
|
||||
"method": "GET",
|
||||
"name": "https://1.bp.blogspot.com/-hkNkoCjc5UA/T4JTlCjhhfI/AAAAAAAAB98/XxQwZ-QPkI8/s1600/Free+Google+Wallpapers+3.jpg",
|
||||
"responseStatus": 200,
|
||||
"timestamp": 1725369200216,
|
||||
"transferSize": 82375,
|
||||
},
|
||||
],
|
||||
},
|
||||
"plugin": "rrweb/network@1",
|
||||
},
|
||||
"seen": 8833798676917222,
|
||||
"timestamp": 1725369200216,
|
||||
"type": 6,
|
||||
"windowId": "0191C63B-03FF-73B5-96BE-40BE2761621C",
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"height": 852,
|
||||
"href": "",
|
||||
"width": 393,
|
||||
},
|
||||
"timestamp": 1725607643113,
|
||||
"type": 4,
|
||||
"windowId": "0191C63B-03FF-73B5-96BE-40BE2761621C",
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"initialOffset": {
|
||||
"left": 0,
|
||||
"top": 0,
|
||||
},
|
||||
"node": {
|
||||
"childNodes": [
|
||||
{
|
||||
"id": 2,
|
||||
"name": "html",
|
||||
"publicId": "",
|
||||
"systemId": "",
|
||||
"type": 1,
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"data-rrweb-id": 3,
|
||||
"style": "height: 100vh; width: 100vw;",
|
||||
},
|
||||
"childNodes": [
|
||||
{
|
||||
"attributes": {
|
||||
"data-rrweb-id": 4,
|
||||
},
|
||||
"childNodes": [
|
||||
{
|
||||
"attributes": {
|
||||
"type": "text/css",
|
||||
},
|
||||
"childNodes": [
|
||||
{
|
||||
"id": 101,
|
||||
"textContent": "
|
||||
body {
|
||||
margin: unset;
|
||||
}
|
||||
input, button, select, textarea {
|
||||
font: inherit;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
background: transparent;
|
||||
padding-block: 0 !important;
|
||||
}
|
||||
.input:focus {
|
||||
outline: none;
|
||||
}
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
",
|
||||
"type": 3,
|
||||
},
|
||||
],
|
||||
"id": 100,
|
||||
"tagName": "style",
|
||||
"type": 2,
|
||||
},
|
||||
],
|
||||
"id": 4,
|
||||
"tagName": "head",
|
||||
"type": 2,
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"data-rrweb-id": 5,
|
||||
"style": "height: 100vh; width: 100vw;",
|
||||
},
|
||||
"childNodes": [
|
||||
{
|
||||
"attributes": {
|
||||
"data-rrweb-id": 4324378400,
|
||||
"height": 852,
|
||||
"src": "data:image/jpeg;base64,/9j/4AAQSkZJR",
|
||||
"style": "width: 393px;height: 852px;position: fixed;left: 0px;top: 0px;",
|
||||
"width": 393,
|
||||
},
|
||||
"childNodes": [],
|
||||
"id": 4324378400,
|
||||
"tagName": "img",
|
||||
"type": 2,
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"data-render-reason": "a fixed placeholder to contain the keyboard in the correct stacking position",
|
||||
"data-rrweb-id": 9,
|
||||
},
|
||||
"childNodes": [],
|
||||
"id": 9,
|
||||
"tagName": "div",
|
||||
"type": 2,
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"data-rrweb-id": 7,
|
||||
},
|
||||
"childNodes": [],
|
||||
"id": 7,
|
||||
"tagName": "div",
|
||||
"type": 2,
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"data-rrweb-id": 11,
|
||||
},
|
||||
"childNodes": [],
|
||||
"id": 11,
|
||||
"tagName": "div",
|
||||
"type": 2,
|
||||
},
|
||||
],
|
||||
"id": 5,
|
||||
"tagName": "body",
|
||||
"type": 2,
|
||||
},
|
||||
],
|
||||
"id": 3,
|
||||
"tagName": "html",
|
||||
"type": 2,
|
||||
},
|
||||
],
|
||||
"id": 1,
|
||||
"type": 0,
|
||||
},
|
||||
},
|
||||
"timestamp": 1725607643113,
|
||||
"type": 2,
|
||||
"windowId": "0191C63B-03FF-73B5-96BE-40BE2761621C",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`snapshot parsing handles normal mobile data 1`] = `
|
||||
[
|
||||
{
|
||||
"data": {
|
||||
"payload": {
|
||||
"requests": [
|
||||
{
|
||||
"duration": 28,
|
||||
"entryType": "resource",
|
||||
"initiatorType": "fetch",
|
||||
"method": "GET",
|
||||
"name": "https://1.bp.blogspot.com/-hkNkoCjc5UA/T4JTlCjhhfI/AAAAAAAAB98/XxQwZ-QPkI8/s1600/Free+Google+Wallpapers+3.jpg",
|
||||
"responseStatus": 200,
|
||||
"timestamp": 1725369200216,
|
||||
"transferSize": 82375,
|
||||
},
|
||||
],
|
||||
},
|
||||
"plugin": "rrweb/network@1",
|
||||
},
|
||||
"seen": 8833798676917222,
|
||||
"timestamp": 1725369200216,
|
||||
"type": 6,
|
||||
"windowId": "0191C63B-03FF-73B5-96BE-40BE2761621C",
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"height": 852,
|
||||
"href": "",
|
||||
"width": 393,
|
||||
},
|
||||
"timestamp": 1725607643113,
|
||||
"type": 4,
|
||||
"windowId": "0191C63B-03FF-73B5-96BE-40BE2761621C",
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"initialOffset": {
|
||||
"left": 0,
|
||||
"top": 0,
|
||||
},
|
||||
"node": {
|
||||
"childNodes": [
|
||||
{
|
||||
"id": 2,
|
||||
"name": "html",
|
||||
"publicId": "",
|
||||
"systemId": "",
|
||||
"type": 1,
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"data-rrweb-id": 3,
|
||||
"style": "height: 100vh; width: 100vw;",
|
||||
},
|
||||
"childNodes": [
|
||||
{
|
||||
"attributes": {
|
||||
"data-rrweb-id": 4,
|
||||
},
|
||||
"childNodes": [
|
||||
{
|
||||
"attributes": {
|
||||
"type": "text/css",
|
||||
},
|
||||
"childNodes": [
|
||||
{
|
||||
"id": 101,
|
||||
"textContent": "
|
||||
body {
|
||||
margin: unset;
|
||||
}
|
||||
input, button, select, textarea {
|
||||
font: inherit;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
background: transparent;
|
||||
padding-block: 0 !important;
|
||||
}
|
||||
.input:focus {
|
||||
outline: none;
|
||||
}
|
||||
img {
|
||||
border-style: none;
|
||||
}
|
||||
",
|
||||
"type": 3,
|
||||
},
|
||||
],
|
||||
"id": 100,
|
||||
"tagName": "style",
|
||||
"type": 2,
|
||||
},
|
||||
],
|
||||
"id": 4,
|
||||
"tagName": "head",
|
||||
"type": 2,
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"data-rrweb-id": 5,
|
||||
"style": "height: 100vh; width: 100vw;",
|
||||
},
|
||||
"childNodes": [
|
||||
{
|
||||
"attributes": {
|
||||
"data-rrweb-id": 4324378400,
|
||||
"height": 852,
|
||||
"src": "data:image/jpeg;base64,/9j/4AAQSkZJR",
|
||||
"style": "width: 393px;height: 852px;position: fixed;left: 0px;top: 0px;",
|
||||
"width": 393,
|
||||
},
|
||||
"childNodes": [],
|
||||
"id": 4324378400,
|
||||
"tagName": "img",
|
||||
"type": 2,
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"data-render-reason": "a fixed placeholder to contain the keyboard in the correct stacking position",
|
||||
"data-rrweb-id": 9,
|
||||
},
|
||||
"childNodes": [],
|
||||
"id": 9,
|
||||
"tagName": "div",
|
||||
"type": 2,
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"data-rrweb-id": 7,
|
||||
},
|
||||
"childNodes": [],
|
||||
"id": 7,
|
||||
"tagName": "div",
|
||||
"type": 2,
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"data-rrweb-id": 11,
|
||||
},
|
||||
"childNodes": [],
|
||||
"id": 11,
|
||||
"tagName": "div",
|
||||
"type": 2,
|
||||
},
|
||||
],
|
||||
"id": 5,
|
||||
"tagName": "body",
|
||||
"type": 2,
|
||||
},
|
||||
],
|
||||
"id": 3,
|
||||
"tagName": "html",
|
||||
"type": 2,
|
||||
},
|
||||
],
|
||||
"id": 1,
|
||||
"type": 0,
|
||||
},
|
||||
},
|
||||
"timestamp": 1725607643113,
|
||||
"type": 2,
|
||||
"windowId": "0191C63B-03FF-73B5-96BE-40BE2761621C",
|
||||
},
|
||||
]
|
||||
`;
|
20
ee/frontend/mobile-replay/parsing.test.ts
Normal file
20
ee/frontend/mobile-replay/parsing.test.ts
Normal file
@ -0,0 +1,20 @@
|
||||
import { parseEncodedSnapshots } from 'scenes/session-recordings/player/sessionRecordingDataLogic'
|
||||
|
||||
import { encodedWebSnapshotData } from './__mocks__/encoded-snapshot-data'
|
||||
|
||||
describe('snapshot parsing', () => {
|
||||
const sessionId = '12345'
|
||||
const numberOfParsedLinesInData = 3
|
||||
|
||||
it('handles normal mobile data', async () => {
|
||||
const parsed = await parseEncodedSnapshots(encodedWebSnapshotData, sessionId, true)
|
||||
expect(parsed.length).toEqual(numberOfParsedLinesInData)
|
||||
expect(parsed).toMatchSnapshot()
|
||||
})
|
||||
it('handles mobile data with no meta event', async () => {
|
||||
const withoutMeta = [encodedWebSnapshotData[0], encodedWebSnapshotData[2]]
|
||||
const parsed = await parseEncodedSnapshots(withoutMeta, sessionId, true)
|
||||
expect(parsed.length).toEqual(numberOfParsedLinesInData)
|
||||
expect(parsed).toMatchSnapshot()
|
||||
})
|
||||
})
|
@ -0,0 +1,4 @@
|
||||
export const encodedWebSnapshotData: string[] = [
|
||||
'{"window_id":"0191c366-dd75-708c-bb41-c0d5bd2bb0dc","data":[{"type":4,"data":{"href":"http://localhost:8000/project/1","width":719,"height":914},"timestamp":1725560859629},{"type":3,"data":{"source":2,"type":0,"id":320,"x":21.41059112548828,"y":28.776042938232422},"timestamp":1725560861395},{"type":3,"data":{"source":2,"type":2,"id":320,"x":21,"y":28,"pointerType":0},"timestamp":1725560861398},{"type":3,"data":{"source":0,"texts":[],"attributes":[{"id":59,"attributes":{"class":"Navbar3000"}},{"id":313,"attributes":{"class":"Navbar3000__overlay"}}],"removes":[],"adds":[]},"timestamp":1725560861402}]}',
|
||||
'{"window_id":"0191c366-dd75-708c-bb41-c0d5bd2bb0dc","data":[{"type":4,"data":{"href":"http://localhost:8000/project/1","width":719,"height":914},"timestamp":1725560859629},{"type":3,"data":{"source":2,"type":0,"id":320,"x":21.41059112548828,"y":28.776042938232422},"timestamp":1725560861395},{"type":3,"data":{"source":2,"type":2,"id":320,"x":21,"y":28,"pointerType":0},"timestamp":1725560861398},{"type":3,"data":{"source":0,"texts":[],"attributes":[{"id":59,"attributes":{"class":"Navbar3000"}},{"id":313,"attributes":{"class":"Navbar3000__overlay"}}],"removes":[],"adds":[]},"timestamp":1725560861402}]}',
|
||||
]
|
@ -2078,3 +2078,193 @@ exports[`sessionRecordingDataLogic deduplicateSnapshots should match snapshot 1`
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`sessionRecordingDataLogic snapshot parsing handles data with unparseable lines 1`] = `
|
||||
[
|
||||
{
|
||||
"data": {
|
||||
"height": 914,
|
||||
"href": "http://localhost:8000/project/1",
|
||||
"width": 719,
|
||||
},
|
||||
"timestamp": 1725560859629,
|
||||
"type": 4,
|
||||
"windowId": "0191c366-dd75-708c-bb41-c0d5bd2bb0dc",
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"id": 320,
|
||||
"source": 2,
|
||||
"type": 0,
|
||||
"x": 21.41059112548828,
|
||||
"y": 28.776042938232422,
|
||||
},
|
||||
"timestamp": 1725560861395,
|
||||
"type": 3,
|
||||
"windowId": "0191c366-dd75-708c-bb41-c0d5bd2bb0dc",
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"id": 320,
|
||||
"pointerType": 0,
|
||||
"source": 2,
|
||||
"type": 2,
|
||||
"x": 21,
|
||||
"y": 28,
|
||||
},
|
||||
"timestamp": 1725560861398,
|
||||
"type": 3,
|
||||
"windowId": "0191c366-dd75-708c-bb41-c0d5bd2bb0dc",
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"adds": [],
|
||||
"attributes": [
|
||||
{
|
||||
"attributes": {
|
||||
"class": "Navbar3000",
|
||||
},
|
||||
"id": 59,
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"class": "Navbar3000__overlay",
|
||||
},
|
||||
"id": 313,
|
||||
},
|
||||
],
|
||||
"removes": [],
|
||||
"source": 0,
|
||||
"texts": [],
|
||||
},
|
||||
"timestamp": 1725560861402,
|
||||
"type": 3,
|
||||
"windowId": "0191c366-dd75-708c-bb41-c0d5bd2bb0dc",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`sessionRecordingDataLogic snapshot parsing handles normal web data 1`] = `
|
||||
[
|
||||
{
|
||||
"data": {
|
||||
"height": 914,
|
||||
"href": "http://localhost:8000/project/1",
|
||||
"width": 719,
|
||||
},
|
||||
"timestamp": 1725560859629,
|
||||
"type": 4,
|
||||
"windowId": "0191c366-dd75-708c-bb41-c0d5bd2bb0dc",
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"id": 320,
|
||||
"source": 2,
|
||||
"type": 0,
|
||||
"x": 21.41059112548828,
|
||||
"y": 28.776042938232422,
|
||||
},
|
||||
"timestamp": 1725560861395,
|
||||
"type": 3,
|
||||
"windowId": "0191c366-dd75-708c-bb41-c0d5bd2bb0dc",
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"id": 320,
|
||||
"pointerType": 0,
|
||||
"source": 2,
|
||||
"type": 2,
|
||||
"x": 21,
|
||||
"y": 28,
|
||||
},
|
||||
"timestamp": 1725560861398,
|
||||
"type": 3,
|
||||
"windowId": "0191c366-dd75-708c-bb41-c0d5bd2bb0dc",
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"adds": [],
|
||||
"attributes": [
|
||||
{
|
||||
"attributes": {
|
||||
"class": "Navbar3000",
|
||||
},
|
||||
"id": 59,
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"class": "Navbar3000__overlay",
|
||||
},
|
||||
"id": 313,
|
||||
},
|
||||
],
|
||||
"removes": [],
|
||||
"source": 0,
|
||||
"texts": [],
|
||||
},
|
||||
"timestamp": 1725560861402,
|
||||
"type": 3,
|
||||
"windowId": "0191c366-dd75-708c-bb41-c0d5bd2bb0dc",
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"height": 914,
|
||||
"href": "http://localhost:8000/project/1",
|
||||
"width": 719,
|
||||
},
|
||||
"timestamp": 1725560859629,
|
||||
"type": 4,
|
||||
"windowId": "0191c366-dd75-708c-bb41-c0d5bd2bb0dc",
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"id": 320,
|
||||
"source": 2,
|
||||
"type": 0,
|
||||
"x": 21.41059112548828,
|
||||
"y": 28.776042938232422,
|
||||
},
|
||||
"timestamp": 1725560861395,
|
||||
"type": 3,
|
||||
"windowId": "0191c366-dd75-708c-bb41-c0d5bd2bb0dc",
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"id": 320,
|
||||
"pointerType": 0,
|
||||
"source": 2,
|
||||
"type": 2,
|
||||
"x": 21,
|
||||
"y": 28,
|
||||
},
|
||||
"timestamp": 1725560861398,
|
||||
"type": 3,
|
||||
"windowId": "0191c366-dd75-708c-bb41-c0d5bd2bb0dc",
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"adds": [],
|
||||
"attributes": [
|
||||
{
|
||||
"attributes": {
|
||||
"class": "Navbar3000",
|
||||
},
|
||||
"id": 59,
|
||||
},
|
||||
{
|
||||
"attributes": {
|
||||
"class": "Navbar3000__overlay",
|
||||
},
|
||||
"id": 313,
|
||||
},
|
||||
],
|
||||
"removes": [],
|
||||
"source": 0,
|
||||
"texts": [],
|
||||
},
|
||||
"timestamp": 1725560861402,
|
||||
"type": 3,
|
||||
"windowId": "0191c366-dd75-708c-bb41-c0d5bd2bb0dc",
|
||||
},
|
||||
]
|
||||
`;
|
||||
|
@ -2,8 +2,10 @@ import { expectLogic } from 'kea-test-utils'
|
||||
import { api, MOCK_TEAM_ID } from 'lib/api.mock'
|
||||
import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
|
||||
import { convertSnapshotsByWindowId } from 'scenes/session-recordings/__mocks__/recording_snapshots'
|
||||
import { encodedWebSnapshotData } from 'scenes/session-recordings/player/__mocks__/encoded-snapshot-data'
|
||||
import {
|
||||
deduplicateSnapshots,
|
||||
parseEncodedSnapshots,
|
||||
sessionRecordingDataLogic,
|
||||
} from 'scenes/session-recordings/player/sessionRecordingDataLogic'
|
||||
import { teamLogic } from 'scenes/teamLogic'
|
||||
@ -404,4 +406,30 @@ describe('sessionRecordingDataLogic', () => {
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('snapshot parsing', () => {
|
||||
const sessionId = '12345'
|
||||
const numberOfParsedLinesInData = 8
|
||||
it('handles normal web data', async () => {
|
||||
const parsed = await parseEncodedSnapshots(encodedWebSnapshotData, sessionId, false)
|
||||
expect(parsed.length).toEqual(numberOfParsedLinesInData)
|
||||
expect(parsed).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('handles data with unparseable lines', async () => {
|
||||
const parsed = await parseEncodedSnapshots(
|
||||
encodedWebSnapshotData.map((line, index) => {
|
||||
return index == 0 ? line.substring(0, line.length / 2) : line
|
||||
}),
|
||||
sessionId,
|
||||
false
|
||||
)
|
||||
|
||||
// unparseable lines are not returned
|
||||
expect(encodedWebSnapshotData.length).toEqual(2)
|
||||
expect(parsed.length).toEqual(numberOfParsedLinesInData / 2)
|
||||
|
||||
expect(parsed).toMatchSnapshot()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
@ -1,5 +1,5 @@
|
||||
import posthogEE from '@posthog/ee/exports'
|
||||
import { customEvent, EventType, eventWithTime } from '@rrweb/types'
|
||||
import { customEvent, EventType, eventWithTime, fullSnapshotEvent } from '@rrweb/types'
|
||||
import { captureException, captureMessage } from '@sentry/react'
|
||||
import {
|
||||
actions,
|
||||
@ -21,6 +21,7 @@ import api from 'lib/api'
|
||||
import { FEATURE_FLAGS } from 'lib/constants'
|
||||
import { Dayjs, dayjs } from 'lib/dayjs'
|
||||
import { featureFlagLogic, FeatureFlagsSet } from 'lib/logic/featureFlagLogic'
|
||||
import { isObject } from 'lib/utils'
|
||||
import { chainToElements } from 'lib/utils/elements-chain'
|
||||
import { eventUsageLogic } from 'lib/utils/eventUsageLogic'
|
||||
import posthog from 'posthog-js'
|
||||
@ -63,6 +64,54 @@ function isRecordingSnapshot(x: unknown): x is RecordingSnapshot {
|
||||
return typeof x === 'object' && x !== null && 'type' in x && 'timestamp' in x
|
||||
}
|
||||
|
||||
/*
|
||||
there was a bug in mobile SDK that didn't consistently send a meta event with a full snapshot.
|
||||
rrweb player hides itself until it has seen the meta event 🤷
|
||||
but we can patch a meta event into the recording data to make it work
|
||||
*/
|
||||
function patchMetaEventIntoMobileData(parsedLines: RecordingSnapshot[]): RecordingSnapshot[] {
|
||||
let fullSnapshotIndex: number = -1
|
||||
let metaIndex: number = -1
|
||||
try {
|
||||
fullSnapshotIndex = parsedLines.findIndex((l) => l.type === EventType.FullSnapshot)
|
||||
metaIndex = parsedLines.findIndex((l) => l.type === EventType.Meta)
|
||||
|
||||
// then we need to patch the meta event into the snapshot data
|
||||
if (fullSnapshotIndex > -1 && metaIndex === -1) {
|
||||
const fullSnapshot = parsedLines[fullSnapshotIndex] as RecordingSnapshot & fullSnapshotEvent & eventWithTime
|
||||
// a full snapshot (particularly from the mobile transformer) has a relatively fixed structure,
|
||||
// but the types exposed by rrweb don't quite cover what we need , so...
|
||||
const mainNode = fullSnapshot.data.node as any
|
||||
const targetNode = mainNode.childNodes[1].childNodes[1].childNodes[0]
|
||||
const { width, height } = targetNode.attributes
|
||||
const metaEvent: RecordingSnapshot = {
|
||||
windowId: fullSnapshot.windowId,
|
||||
type: EventType.Meta,
|
||||
timestamp: fullSnapshot.timestamp,
|
||||
data: {
|
||||
href: getHrefFromSnapshot(fullSnapshot) || '',
|
||||
width,
|
||||
height,
|
||||
},
|
||||
}
|
||||
parsedLines.splice(fullSnapshotIndex, 0, metaEvent)
|
||||
}
|
||||
} catch (e) {
|
||||
captureException(e, {
|
||||
tags: { feature: 'session-recording-missing-meta-patching' },
|
||||
extra: { fullSnapshotIndex, metaIndex },
|
||||
})
|
||||
}
|
||||
|
||||
return parsedLines
|
||||
}
|
||||
|
||||
function hasAnyWireframes(snapshotData: Record<string, any>[]): boolean {
|
||||
return snapshotData.some((d) => {
|
||||
return isObject(d.data) && 'wireframes' in d.data
|
||||
})
|
||||
}
|
||||
|
||||
export const parseEncodedSnapshots = async (
|
||||
items: (RecordingSnapshot | EncodedRecordingSnapshot | string)[],
|
||||
sessionId: string,
|
||||
@ -72,9 +121,12 @@ export const parseEncodedSnapshots = async (
|
||||
if (!postHogEEModule) {
|
||||
postHogEEModule = await posthogEE()
|
||||
}
|
||||
|
||||
const lineCount = items.length
|
||||
const unparseableLines: string[] = []
|
||||
const parsedLines = items.flatMap((l) => {
|
||||
let isMobileSnapshots = false
|
||||
|
||||
const parsedLines: RecordingSnapshot[] = items.flatMap((l) => {
|
||||
if (!l) {
|
||||
// blob files have an empty line at the end
|
||||
return []
|
||||
@ -83,6 +135,10 @@ export const parseEncodedSnapshots = async (
|
||||
const snapshotLine = typeof l === 'string' ? (JSON.parse(l) as EncodedRecordingSnapshot) : l
|
||||
const snapshotData = isRecordingSnapshot(snapshotLine) ? [snapshotLine] : snapshotLine['data']
|
||||
|
||||
if (!isMobileSnapshots) {
|
||||
isMobileSnapshots = hasAnyWireframes(snapshotData)
|
||||
}
|
||||
|
||||
return snapshotData.map((d: unknown) => {
|
||||
const snap = withMobileTransformer
|
||||
? postHogEEModule?.mobileReplay?.transformEventToWeb(d) || (d as eventWithTime)
|
||||
@ -118,11 +174,13 @@ export const parseEncodedSnapshots = async (
|
||||
})
|
||||
}
|
||||
|
||||
return parsedLines
|
||||
return isMobileSnapshots ? patchMetaEventIntoMobileData(parsedLines) : parsedLines
|
||||
}
|
||||
|
||||
const getHrefFromSnapshot = (snapshot: RecordingSnapshot): string | undefined => {
|
||||
return (snapshot.data as any)?.href || (snapshot.data as any)?.payload?.href
|
||||
const getHrefFromSnapshot = (snapshot: unknown): string | undefined => {
|
||||
return isObject(snapshot) && 'data' in snapshot
|
||||
? (snapshot.data as any)?.href || (snapshot.data as any)?.payload?.href
|
||||
: undefined
|
||||
}
|
||||
|
||||
/*
|
||||
@ -500,12 +558,12 @@ export const sessionRecordingDataLogic = kea<sessionRecordingDataLogicType>([
|
||||
try {
|
||||
const query: HogQLQuery = {
|
||||
kind: NodeKind.HogQLQuery,
|
||||
query: hogql`SELECT properties, uuid
|
||||
FROM events
|
||||
WHERE timestamp > ${dayjs(event.timestamp).subtract(1000, 'ms')}
|
||||
AND timestamp < ${dayjs(event.timestamp).add(1000, 'ms')}
|
||||
AND event = ${event.event}
|
||||
AND uuid = ${event.id}`,
|
||||
query: hogql`SELECT properties, uuid
|
||||
FROM events
|
||||
WHERE timestamp > ${dayjs(event.timestamp).subtract(1000, 'ms')}
|
||||
AND timestamp < ${dayjs(event.timestamp).add(1000, 'ms')}
|
||||
AND event = ${event.event}
|
||||
AND uuid = ${event.id}`,
|
||||
}
|
||||
const response = await api.query(query)
|
||||
if (response.error) {
|
||||
|
Loading…
Reference in New Issue
Block a user