import {MouseEvent, useEffect, useMemo} from 'react'; import queryString from 'query-string'; import ObjectInspector from 'sentry/components/objectInspector'; import {useReplayContext} from 'sentry/components/replays/replayContext'; import {t} from 'sentry/locale'; import {formatBytesBase10} from 'sentry/utils'; import { getFrameMethod, getFrameStatus, isRequestFrame, } from 'sentry/utils/replays/resourceFrame'; import type {SpanFrame} from 'sentry/utils/replays/types'; import { Indent, keyValueTableOrNotFound, SectionItem, SizeTooltip, Warning, } from 'sentry/views/replays/detail/network/details/components'; import {useDismissReqRespBodiesAlert} from 'sentry/views/replays/detail/network/details/onboarding'; import TimestampButton from 'sentry/views/replays/detail/timestampButton'; export type SectionProps = { item: SpanFrame; projectId: string; startTimestampMs: number; }; const UNKNOWN_STATUS = 'unknown'; export function GeneralSection({item, startTimestampMs}: SectionProps) { const {setCurrentTime} = useReplayContext(); const requestFrame = isRequestFrame(item) ? item : null; // TODO[replay]: what about: // `requestFrame?.data?.request?.size` vs. `requestFrame?.data?.requestBodySize` const data = { [t('URL')]: item.description, [t('Type')]: item.op, [t('Method')]: getFrameMethod(item), [t('Status Code')]: String(getFrameStatus(item) ?? UNKNOWN_STATUS), [t('Request Body Size')]: ( {formatBytesBase10(requestFrame?.data?.request?.size ?? 0)} ), [t('Response Body Size')]: ( {formatBytesBase10(requestFrame?.data?.response?.size ?? 0)} ), [t('Duration')]: `${(item.endTimestampMs - item.timestampMs).toFixed(2)}ms`, [t('Timestamp')]: ( { event.stopPropagation(); setCurrentTime(item.offsetMs); }} startTimestampMs={startTimestampMs} timestampMs={item.timestampMs} /> ), }; return ( {keyValueTableOrNotFound(data, t('Missing request details'))} ); } export function RequestHeadersSection({item}: SectionProps) { const data = isRequestFrame(item) ? item.data : {}; return ( {keyValueTableOrNotFound(data.request?.headers, t('Headers not captured'))} ); } export function ResponseHeadersSection({item}: SectionProps) { const data = isRequestFrame(item) ? item.data : {}; return ( {keyValueTableOrNotFound(data.response?.headers, t('Headers not captured'))} ); } export function QueryParamsSection({item}: SectionProps) { const queryParams = queryString.parse(item.description?.split('?')?.[1] ?? ''); return ( ); } export function RequestPayloadSection({item}: SectionProps) { const {dismiss, isDismissed} = useDismissReqRespBodiesAlert(); const data = useMemo(() => (isRequestFrame(item) ? item.data : {}), [item]); useEffect(() => { if (!isDismissed && 'request' in data) { dismiss(); } }, [dismiss, data, isDismissed]); return ( {t('Size:')} {formatBytesBase10(data.request?.size ?? 0)} } > {'request' in data ? ( ) : ( t('Request body not found.') )} ); } export function ResponsePayloadSection({item}: SectionProps) { const {dismiss, isDismissed} = useDismissReqRespBodiesAlert(); const data = useMemo(() => (isRequestFrame(item) ? item.data : {}), [item]); useEffect(() => { if (!isDismissed && 'response' in data) { dismiss(); } }, [dismiss, data, isDismissed]); return ( {t('Size:')} {formatBytesBase10(data.response?.size ?? 0)} } > {'response' in data ? ( ) : ( t('Response body not found.') )} ); }