feat(frontend):maintain new NetworkRequest message & fix request/response payload view & fix sess files download logic

This commit is contained in:
Alex Kaminskii 2022-12-09 19:22:01 +01:00
parent f37757f2d2
commit b819e86945
5 changed files with 72 additions and 58 deletions

View file

@ -29,6 +29,7 @@ const OTHER = 'other';
const TYPE_TO_TAB = {
[TYPES.XHR]: XHR,
[TYPES.FETCH]: XHR,
[TYPES.JS]: JS,
[TYPES.CSS]: CSS,
[TYPES.IMG]: IMG,

View file

@ -8,6 +8,11 @@ const REQUEST = 'REQUEST';
const RESPONSE = 'RESPONSE';
const TABS = [HEADERS, REQUEST, RESPONSE].map((tab) => ({ text: tab, key: tab }));
function isValidJSON(o: any): o is Object {
return typeof o === "object" && o != null
}
interface Props {
resource: any;
}
@ -15,37 +20,41 @@ function FetchTabs(props: Props) {
const { resource } = props;
const [activeTab, setActiveTab] = useState(HEADERS);
const onTabClick = (tab: string) => setActiveTab(tab);
const [jsonPayload, setJsonPayload] = useState(null);
const [jsonResponse, setJsonResponse] = useState(null);
const [jsonRequest, setJsonRequest] = useState<Object | string | null>(null);
const [jsonResponse, setJsonResponse] = useState<Object | string | null>(null);
const [requestHeaders, setRequestHeaders] = useState(null);
const [responseHeaders, setResponseHeaders] = useState(null);
useEffect(() => {
const { payload, response } = resource;
const { request, response } = resource;
try {
let jsonPayload = typeof payload === 'string' ? JSON.parse(payload) : payload;
let requestHeaders = jsonPayload.headers;
jsonPayload.body =
typeof jsonPayload.body === 'string' ? JSON.parse(jsonPayload.body) : jsonPayload.body;
delete jsonPayload.headers;
setJsonPayload(jsonPayload);
setRequestHeaders(requestHeaders);
} catch (e) {}
let jRequest = JSON.parse(request)
setRequestHeaders(jRequest.headers);
try {
let jBody = JSON.parse(jRequest.body)
jBody = isValidJSON(jBody) ? jBody : jRequest.body
setJsonRequest(jBody)
} catch {
setJsonRequest(jRequest.body)
}
} catch {}
try {
let jsonResponse = typeof response === 'string' ? JSON.parse(response) : response;
let responseHeaders = jsonResponse.headers;
jsonResponse.body =
typeof jsonResponse.body === 'string' ? JSON.parse(jsonResponse.body) : jsonResponse.body;
delete jsonResponse.headers;
setJsonResponse(jsonResponse);
setResponseHeaders(responseHeaders);
} catch (e) {}
}, [resource, activeTab]);
let jResponse = JSON.parse(response)
setResponseHeaders(jResponse.headers);
try {
let jBody = JSON.parse(jResponse.body)
jBody = isValidJSON(jBody) ? jBody : jResponse.body
setJsonResponse(jBody)
} catch {
setJsonResponse(jResponse.body)
}
} catch {}
}, [resource]);
const renderActiveTab = () => {
const { payload, response } = resource;
const { request, response } = resource;
switch (activeTab) {
case REQUEST:
return (
@ -57,15 +66,15 @@ function FetchTabs(props: Props) {
</div>
}
size="small"
show={!payload}
show={!request}
// animatedIcon="no-results"
>
<div>
<div className="mt-6">
{jsonPayload === undefined ? (
<div className="ml-3 break-words my-3"> {payload} </div>
{ !isValidJSON(jsonRequest) ? (
<div className="ml-3 break-words my-3"> {jsonRequest || request} </div>
) : (
<JSONTree src={jsonPayload} collapsed={false} enableClipboard />
<JSONTree src={jsonRequest} collapsed={false} enableClipboard />
)}
</div>
<div className="divider" />
@ -87,8 +96,8 @@ function FetchTabs(props: Props) {
>
<div>
<div className="mt-6">
{jsonResponse === undefined ? (
<div className="ml-3 break-words my-3"> {response} </div>
{ !isValidJSON(jsonResponse) ? (
<div className="ml-3 break-words my-3"> {jsonResponse || response} </div>
) : (
<JSONTree src={jsonResponse} collapsed={false} enableClipboard />
)}

View file

@ -12,6 +12,15 @@ export default class ListWalker<T extends Timed> {
this.list.push(m);
}
insert(m: T): void {
let index = this.list.findIndex(om => om.time > m.time)
if (index === -1) {
index = this.length
}
const oldList = this.list
this._list = [...oldList.slice(0, index), m, ...oldList.slice(index)]
}
reset(): void {
this.p = 0
}

View file

@ -2,7 +2,7 @@
import { Decoder } from "syncod";
import logger from 'App/logger';
import Resource, { TYPES } from 'Types/session/resource';
import Resource, { TYPES as RES_TYPES } from 'Types/session/resource';
import { TYPES as EVENT_TYPES } from 'Types/session/event';
import { Log } from './types';
@ -187,13 +187,12 @@ export default class MessageManager {
const stateToUpdate : Partial<State>= {
performanceChartData: this.performanceTrackManager.chartData,
performanceAvaliability: this.performanceTrackManager.avaliability,
...this.lists.getFullListsState()
...this.lists.getFullListsState(),
}
if (this.activityManager) {
this.activityManager.end()
stateToUpdate.skipIntervals = this.activityManager.list
}
this.state.update(stateToUpdate)
}
private onFileReadFailed = (e: any) => {
@ -227,26 +226,18 @@ export default class MessageManager {
this.setMessagesLoading(true)
this.waitingForFiles = true
try {
if (this.session.domURL && this.session.domURL.length > 0) {
await loadFiles(this.session.domURL, createNewParser())
this.onFileReadSuccess()
} else {
const file = await requestEFSDom(this.session.sessionId)
const parser = createNewParser(false)
await parser(file)
}
} catch (e) {
console.error('Cant get session replay file:', e)
try {
// back compat with old mobsUrl
await loadFiles(this.session.mobsUrl, createNewParser(false))
} catch (e) {
this.onFileReadFailed(e)
}
} finally {
this.onFileReadFinally()
}
let fileReadPromise = this.session.domURL && this.session.domURL.length > 0
? loadFiles(this.session.domURL, createNewParser())
: requestEFSDom(this.session.sessionId)
.then(createNewParser(false))
fileReadPromise.catch(e => {
logger.error('Can not get normal session replay file:', e)
// back compat fallback to an old mobsUrl
return loadFiles(this.session.mobsUrl, createNewParser(false))
})
.then(this.onFileReadSuccess)
.catch(this.onFileReadFailed)
.finally(this.onFileReadFinally)
// load devtools
if (this.session.devtoolsURL.length) {
@ -256,7 +247,10 @@ export default class MessageManager {
requestEFSDevtools(this.session.sessionId)
.then(createNewParser(false))
)
//.catch() // not able to download the devtools file
.then(() => {
this.state.update(this.lists.getFullListsState())
})
.catch(e => logger.error("Can not download the devtools file", e))
.finally(() => this.state.update({ devtoolsLoading: false }))
}
}
@ -439,15 +433,16 @@ export default class MessageManager {
)
break;
case MType.Fetch:
case MType.NetworkRequest:
// @ts-ignore burn immutable
this.lists.lists.fetch.append(Resource({
this.lists.lists.fetch.insert(Resource({
method: msg.method,
url: msg.url,
payload: msg.request,
request: msg.request,
response: msg.response,
status: msg.status,
duration: msg.duration,
type: TYPES.XHR,
type: msg.type === "xhr" ? RES_TYPES.XHR : RES_TYPES.FETCH,
time: Math.max(msg.timestamp - this.sessionStart, 0), // !!! doesn't look good. TODO: find solution to show negative timings
index,
}) as Timed)

View file

@ -3,6 +3,7 @@ import Record from 'Types/Record';
import { getResourceName } from 'App/utils';
const XHR = 'xhr';
const FETCH = 'fetch';
const JS = 'script';
const CSS = 'css';
const IMG = 'img';
@ -32,7 +33,6 @@ const OTHER = 'other';
const TYPES_MAP = {
"stylesheet": CSS,
"fetch": XHR,
}
function getResourceStatus(status, success) {
@ -53,6 +53,7 @@ function getResourceSuccess(success, status) {
export const TYPES = {
XHR,
FETCH,
JS,
CSS,
IMG,
@ -85,7 +86,7 @@ export default Record({
// initiator: "other",
// pagePath: "",
method: '',
payload:'',
request:'',
response: '',
headerSize: 0,
encodedBodySize: 0,
@ -93,14 +94,13 @@ export default Record({
responseBodySize: 0,
timings: List(),
}, {
fromJS: ({ responseBody, response, type, initiator, status, success, time, datetime, timestamp, timings, ...resource }) => ({
fromJS: ({ type, initiator, status, success, time, datetime, timestamp, timings, ...resource }) => ({
...resource,
type: TYPES_MAP[type] || type,
name: getResourceName(resource.url),
status: getResourceStatus(status, success),
success: getResourceSuccess(success, status),
time: typeof time === 'number' ? time : datetime || timestamp,
response: responseBody || response,
ttfb: timings && timings.ttfb,
timewidth: timings && timings.timewidth,
timings,