feat: Use JSON viewer for Network Body (#428)

This commit is contained in:
Ernest Iliiasov 2024-06-05 12:16:07 -07:00 committed by GitHub
parent 0fa131f4b9
commit 3b29721254
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 131 additions and 1 deletions

View file

@ -0,0 +1,5 @@
---
'@hyperdx/app': patch
---
Render JSON network body in a JSON viewer

View file

@ -0,0 +1,107 @@
import type { Meta } from '@storybook/react';
import { NetworkBody } from './LogSidePanelElements';
const meta: Meta = {
title: 'LogSidePanelElements',
component: NetworkBody,
parameters: {},
};
const MOCK_SQL_BODY = `SELECT
toUnixTimestamp(toStartOfInterval(timestamp, INTERVAL '10 second')) as ts_bucket,
if(multiSearchAny(severity_text, ['err', 'emerg', 'alert', 'crit', 'fatal']), 'error', 'info') as severity_group,
count(*) as count
FROM default.log_stream
WHERE (1 = 1) AND ((_timestamp_sort_key >= 1717609920000000000 AND _timestamp_sort_key < 1717610820000000000) AND ((type = 'span')))
GROUP BY ts_bucket, severity_group
ORDER BY ts_bucket
WITH FILL
FROM toUnixTimestamp(toStartOfInterval(toDateTime(1717609920), INTERVAL '10 second'))
TO toUnixTimestamp(toStartOfInterval(toDateTime(1717610820), INTERVAL '10 second'))
STEP 10
LIMIT 1000
FORMAT JSON`;
const MOCK_JSON_BODY = `{
"links": {
"self": "http://example.com/articles",
"next": "http://example.com/articles?page[offset]=2",
"last": "http://example.com/articles?page[offset]=10"
},
"data": [{
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON:API paints my bikeshed!"
},
"relationships": {
"author": {
"links": {
"self": "http://example.com/articles/1/relationships/author",
"related": "http://example.com/articles/1/author"
},
"data": { "type": "people", "id": "9" }
},
"comments": {
"links": {
"self": "http://example.com/articles/1/relationships/comments",
"related": "http://example.com/articles/1/comments"
},
"data": [
{ "type": "comments", "id": "5" },
{ "type": "comments", "id": "12" }
]
}
},
"links": {
"self": "http://example.com/articles/1"
}
}],
"included": [{
"type": "people",
"id": "9",
"attributes": {
"firstName": "Dan",
"lastName": "Gebhardt",
"twitter": "dgeb"
},
"links": {
"self": "http://example.com/people/9"
}
}, {
"type": "comments",
"id": "5",
"attributes": {
"body": "First!"
},
"relationships": {
"author": {
"data": { "type": "people", "id": "2" }
}
},
"links": {
"self": "http://example.com/comments/5"
}
}, {
"type": "comments",
"id": "12",
"attributes": {
"body": "I like XML better"
},
"relationships": {
"author": {
"data": { "type": "people", "id": "9" }
}
},
"links": {
"self": "http://example.com/comments/12"
}
}]
}`;
export const Sql = () => <NetworkBody body={MOCK_SQL_BODY} />;
export const Json = () => <NetworkBody body={MOCK_JSON_BODY} />;
export default meta;

View file

@ -4,6 +4,7 @@ import { CloseButton } from 'react-bootstrap';
import { JSONTree } from 'react-json-tree';
import { ColumnDef, Row } from '@tanstack/react-table';
import HyperJson from './components/HyperJson';
import { TableCellButton } from './components/Table';
import { UNDEFINED_WIDTH } from './tableUtils';
import type { StacktraceBreadcrumb, StacktraceFrame } from './types';
@ -388,6 +389,21 @@ export const NetworkBody = ({
);
}, []);
const parsedBody = React.useMemo(() => {
if (typeof body !== 'string') return null;
try {
if (
(body.startsWith('{') && body.endsWith('}')) ||
(body.startsWith('[') && body.endsWith(']'))
) {
const parsed = JSON.parse(body);
return parsed;
}
} catch (e) {
return null;
}
}, [body]);
return (
<>
{body != null && body != '' ? (
@ -399,7 +415,9 @@ export const NetworkBody = ({
whiteSpace: 'pre-wrap',
}}
>
{typeof body === 'string' ? (
{parsedBody ? (
<HyperJson data={parsedBody} normallyExpanded />
) : typeof body === 'string' ? (
body
) : (
<JSONTree