|
@@ -8,6 +8,10 @@ import {useReplayContext} from 'sentry/components/replays/replayContext';
|
|
import {t} from 'sentry/locale';
|
|
import {t} from 'sentry/locale';
|
|
import {space} from 'sentry/styles/space';
|
|
import {space} from 'sentry/styles/space';
|
|
import {formatBytesBase10} from 'sentry/utils';
|
|
import {formatBytesBase10} from 'sentry/utils';
|
|
|
|
+import {
|
|
|
|
+ NetworkMetaWarning,
|
|
|
|
+ ReplayNetworkRequestOrResponse,
|
|
|
|
+} from 'sentry/utils/replays/replay';
|
|
import {
|
|
import {
|
|
getFrameMethod,
|
|
getFrameMethod,
|
|
getFrameStatus,
|
|
getFrameStatus,
|
|
@@ -24,6 +28,7 @@ import {
|
|
Warning,
|
|
Warning,
|
|
} from 'sentry/views/replays/detail/network/details/components';
|
|
} from 'sentry/views/replays/detail/network/details/components';
|
|
import {useDismissReqRespBodiesAlert} from 'sentry/views/replays/detail/network/details/onboarding';
|
|
import {useDismissReqRespBodiesAlert} from 'sentry/views/replays/detail/network/details/onboarding';
|
|
|
|
+import {fixJson} from 'sentry/views/replays/detail/network/truncateJson/fixJson';
|
|
import TimestampButton from 'sentry/views/replays/detail/timestampButton';
|
|
import TimestampButton from 'sentry/views/replays/detail/timestampButton';
|
|
|
|
|
|
export type SectionProps = {
|
|
export type SectionProps = {
|
|
@@ -39,9 +44,6 @@ export function GeneralSection({item, startTimestampMs}: SectionProps) {
|
|
|
|
|
|
const requestFrame = isRequestFrame(item) ? item : null;
|
|
const requestFrame = isRequestFrame(item) ? item : null;
|
|
|
|
|
|
- // TODO[replay]: what about:
|
|
|
|
- // `requestFrame?.data?.request?.size` vs. `requestFrame?.data?.requestBodySize`
|
|
|
|
-
|
|
|
|
const data: KeyValueTuple[] = [
|
|
const data: KeyValueTuple[] = [
|
|
{key: t('URL'), value: item.description},
|
|
{key: t('URL'), value: item.description},
|
|
{key: t('Type'), value: item.op},
|
|
{key: t('Type'), value: item.op},
|
|
@@ -179,6 +181,8 @@ export function RequestPayloadSection({item}: SectionProps) {
|
|
const {dismiss, isDismissed} = useDismissReqRespBodiesAlert();
|
|
const {dismiss, isDismissed} = useDismissReqRespBodiesAlert();
|
|
|
|
|
|
const data = useMemo(() => (isRequestFrame(item) ? item.data : {}), [item]);
|
|
const data = useMemo(() => (isRequestFrame(item) ? item.data : {}), [item]);
|
|
|
|
+ const {warnings, body} = getBodyAndWarnings(data.request);
|
|
|
|
+
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
if (!isDismissed && 'request' in data) {
|
|
if (!isDismissed && 'request' in data) {
|
|
dismiss();
|
|
dismiss();
|
|
@@ -195,9 +199,9 @@ export function RequestPayloadSection({item}: SectionProps) {
|
|
}
|
|
}
|
|
>
|
|
>
|
|
<Indent>
|
|
<Indent>
|
|
- <Warning warnings={data.request?._meta?.warnings} />
|
|
|
|
|
|
+ <Warning warnings={warnings} />
|
|
{'request' in data ? (
|
|
{'request' in data ? (
|
|
- <ObjectInspector data={data.request?.body} expandLevel={2} showCopyButton />
|
|
|
|
|
|
+ <ObjectInspector data={body} expandLevel={2} showCopyButton />
|
|
) : (
|
|
) : (
|
|
t('Request body not found.')
|
|
t('Request body not found.')
|
|
)}
|
|
)}
|
|
@@ -210,6 +214,8 @@ export function ResponsePayloadSection({item}: SectionProps) {
|
|
const {dismiss, isDismissed} = useDismissReqRespBodiesAlert();
|
|
const {dismiss, isDismissed} = useDismissReqRespBodiesAlert();
|
|
|
|
|
|
const data = useMemo(() => (isRequestFrame(item) ? item.data : {}), [item]);
|
|
const data = useMemo(() => (isRequestFrame(item) ? item.data : {}), [item]);
|
|
|
|
+ const {warnings, body} = getBodyAndWarnings(data.response);
|
|
|
|
+
|
|
useEffect(() => {
|
|
useEffect(() => {
|
|
if (!isDismissed && 'response' in data) {
|
|
if (!isDismissed && 'response' in data) {
|
|
dismiss();
|
|
dismiss();
|
|
@@ -226,9 +232,9 @@ export function ResponsePayloadSection({item}: SectionProps) {
|
|
}
|
|
}
|
|
>
|
|
>
|
|
<Indent>
|
|
<Indent>
|
|
- <Warning warnings={data?.response?._meta?.warnings} />
|
|
|
|
|
|
+ <Warning warnings={warnings} />
|
|
{'response' in data ? (
|
|
{'response' in data ? (
|
|
- <ObjectInspector data={data.response?.body} expandLevel={2} showCopyButton />
|
|
|
|
|
|
+ <ObjectInspector data={body} expandLevel={2} showCopyButton />
|
|
) : (
|
|
) : (
|
|
t('Response body not found.')
|
|
t('Response body not found.')
|
|
)}
|
|
)}
|
|
@@ -236,3 +242,29 @@ export function ResponsePayloadSection({item}: SectionProps) {
|
|
</SectionItem>
|
|
</SectionItem>
|
|
);
|
|
);
|
|
}
|
|
}
|
|
|
|
+
|
|
|
|
+function getBodyAndWarnings(reqOrRes?: ReplayNetworkRequestOrResponse): {
|
|
|
|
+ body: ReplayNetworkRequestOrResponse['body'];
|
|
|
|
+ warnings: NetworkMetaWarning[];
|
|
|
|
+} {
|
|
|
|
+ if (!reqOrRes) {
|
|
|
|
+ return {body: undefined, warnings: []};
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ const warnings = reqOrRes._meta?.warnings ?? [];
|
|
|
|
+ let body = reqOrRes.body;
|
|
|
|
+
|
|
|
|
+ if (typeof body === 'string' && warnings.includes('MAYBE_JSON_TRUNCATED')) {
|
|
|
|
+ try {
|
|
|
|
+ const json = fixJson(body);
|
|
|
|
+ body = JSON.parse(json);
|
|
|
|
+ warnings.push('JSON_TRUNCATED');
|
|
|
|
+ } catch {
|
|
|
|
+ // this can fail, in which case we just use the body string
|
|
|
|
+ warnings.push('INVALID_JSON');
|
|
|
|
+ warnings.push('TEXT_TRUNCATED');
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return {body, warnings};
|
|
|
|
+}
|