mirror of
https://github.com/kubeshark/kubeshark.git
synced 2025-07-17 01:41:56 +00:00
Use react-lowlight
to highlight and json-beautify
, xml-formatter
to prettify the EntryBodySection
(#554)
* Use `react-lowlight` to highlight and `json-beautify` to prettify the `EntryBodySection` * Bring back the line numbers * Make the Base64 decoding optional but make it `true` by default * Align line numbers to right and don't have a dot character * Make line numbers semi transparent * Make `markers` code more elegant * Prettify XML as well
This commit is contained in:
parent
e358aa4c8f
commit
3a83531590
66
ui/package-lock.json
generated
66
ui/package-lock.json
generated
@ -7747,9 +7747,9 @@
|
|||||||
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
|
"integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
|
||||||
},
|
},
|
||||||
"highlight.js": {
|
"highlight.js": {
|
||||||
"version": "10.7.2",
|
"version": "11.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.2.tgz",
|
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.3.1.tgz",
|
||||||
"integrity": "sha512-oFLl873u4usRM9K63j4ME9u3etNF0PLiJhSQ8rdfuL51Wn3zkD6drf9ZW0dOzjnZI22YYG24z30JcmfCZjMgYg=="
|
"integrity": "sha512-PUhCRnPjLtiLHZAQ5A/Dt5F8cWZeMyj9KRsACsWT+OD6OP0x6dp5OmT5jdx0JgEyPxPZZIPQpRN2TciUT7occw=="
|
||||||
},
|
},
|
||||||
"hmac-drbg": {
|
"hmac-drbg": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
@ -10234,6 +10234,11 @@
|
|||||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
|
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz",
|
||||||
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
|
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
|
||||||
},
|
},
|
||||||
|
"json-beautify": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-beautify/-/json-beautify-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-17j+Hk2lado0xqKtUcyAjK0AtoHnPSIgktWRsEXgdFQFG9UnaGw6CHa0J7xsvulxRpFl6CrkDFHght1p5ZJc4A=="
|
||||||
|
},
|
||||||
"json-parse-better-errors": {
|
"json-parse-better-errors": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz",
|
||||||
@ -10612,6 +10617,13 @@
|
|||||||
"requires": {
|
"requires": {
|
||||||
"fault": "^1.0.0",
|
"fault": "^1.0.0",
|
||||||
"highlight.js": "~10.7.0"
|
"highlight.js": "~10.7.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"highlight.js": {
|
||||||
|
"version": "10.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
|
||||||
|
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lru-cache": {
|
"lru-cache": {
|
||||||
@ -13577,6 +13589,34 @@
|
|||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||||
},
|
},
|
||||||
|
"react-lowlight": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-lowlight/-/react-lowlight-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-s0+T81PsCbUZYd/0XrplGc6kQEUdiwLKI0G6umJP1ViqRoZRCvSuHvXOy20Usd2ywDKWLuVETQgBDPeNQhPNZg==",
|
||||||
|
"requires": {
|
||||||
|
"lowlight": "^2.4.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"fault": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/fault/-/fault-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-WtySTkS4OKev5JtpHXnib4Gxiurzh5NCGvWrFaZ34m6JehfTUhKZvn9njTfw48t6JumVQOmrKqpmGcdwxnhqBQ==",
|
||||||
|
"requires": {
|
||||||
|
"format": "^0.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lowlight": {
|
||||||
|
"version": "2.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/lowlight/-/lowlight-2.4.1.tgz",
|
||||||
|
"integrity": "sha512-mQkAG0zGQ9lcYecEft+hl9uV1fD6HpURA83/TYrsxKvb8xX2mfyB+aaV/A/aWmhhEcWVzr9Cc+l/fvUYfEUumw==",
|
||||||
|
"requires": {
|
||||||
|
"@types/hast": "^2.0.0",
|
||||||
|
"fault": "^2.0.0",
|
||||||
|
"highlight.js": "~11.3.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-refresh": {
|
"react-refresh": {
|
||||||
"version": "0.8.3",
|
"version": "0.8.3",
|
||||||
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz",
|
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz",
|
||||||
@ -13663,6 +13703,13 @@
|
|||||||
"lowlight": "^1.17.0",
|
"lowlight": "^1.17.0",
|
||||||
"prismjs": "^1.22.0",
|
"prismjs": "^1.22.0",
|
||||||
"refractor": "^3.2.0"
|
"refractor": "^3.2.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"highlight.js": {
|
||||||
|
"version": "10.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz",
|
||||||
|
"integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A=="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"react-toastify": {
|
"react-toastify": {
|
||||||
@ -18149,11 +18196,24 @@
|
|||||||
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz",
|
"resolved": "https://registry.npmjs.org/ws/-/ws-7.4.5.tgz",
|
||||||
"integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g=="
|
"integrity": "sha512-xzyu3hFvomRfXKH8vOFMU3OguG6oOvhXMo3xsGy3xWExqaM2dxBbVxuD99O7m3ZUFMvvscsZDqxfgMaRr/Nr1g=="
|
||||||
},
|
},
|
||||||
|
"xml-formatter": {
|
||||||
|
"version": "2.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml-formatter/-/xml-formatter-2.6.0.tgz",
|
||||||
|
"integrity": "sha512-+bQeoiE5W3CJdDCHTlveYSWFfQWnYB3uHGeRJ6LlEsL5kT++mWy9iN1cMeEDfBbgOnXO2DNUbmQ6elkR/mCcjg==",
|
||||||
|
"requires": {
|
||||||
|
"xml-parser-xo": "^3.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"xml-name-validator": {
|
"xml-name-validator": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz",
|
||||||
"integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw=="
|
"integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw=="
|
||||||
},
|
},
|
||||||
|
"xml-parser-xo": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/xml-parser-xo/-/xml-parser-xo-3.2.0.tgz",
|
||||||
|
"integrity": "sha512-8LRU6cq+d7mVsoDaMhnkkt3CTtAs4153p49fRo+HIB3I1FD1o5CeXRjRH29sQevIfVJIcPjKSsPU/+Ujhq09Rg=="
|
||||||
|
},
|
||||||
"xmlchars": {
|
"xmlchars": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
"@types/react-dom": "^17.0.3",
|
"@types/react-dom": "^17.0.3",
|
||||||
"@uiw/react-textarea-code-editor": "^1.4.12",
|
"@uiw/react-textarea-code-editor": "^1.4.12",
|
||||||
"axios": "^0.21.1",
|
"axios": "^0.21.1",
|
||||||
|
"highlight.js": "^11.3.1",
|
||||||
|
"json-beautify": "^1.1.1",
|
||||||
"jsonpath": "^1.1.1",
|
"jsonpath": "^1.1.1",
|
||||||
"moment": "^2.29.1",
|
"moment": "^2.29.1",
|
||||||
"node-sass": "^5.0.0",
|
"node-sass": "^5.0.0",
|
||||||
@ -23,12 +25,14 @@
|
|||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-copy-to-clipboard": "^5.0.3",
|
"react-copy-to-clipboard": "^5.0.3",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
|
"react-lowlight": "^3.0.0",
|
||||||
"react-scripts": "4.0.3",
|
"react-scripts": "4.0.3",
|
||||||
"react-scrollable-feed-virtualized": "^1.4.9",
|
"react-scrollable-feed-virtualized": "^1.4.9",
|
||||||
"react-syntax-highlighter": "^15.4.3",
|
"react-syntax-highlighter": "^15.4.3",
|
||||||
"react-toastify": "^8.0.3",
|
"react-toastify": "^8.0.3",
|
||||||
"typescript": "^4.2.4",
|
"typescript": "^4.2.4",
|
||||||
"web-vitals": "^1.1.1"
|
"web-vitals": "^1.1.1",
|
||||||
|
"xml-formatter": "^2.6.0"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "react-scripts start",
|
"start": "react-scripts start",
|
||||||
|
@ -6,6 +6,8 @@ import FancyTextDisplay from "../UI/FancyTextDisplay";
|
|||||||
import Queryable from "../UI/Queryable";
|
import Queryable from "../UI/Queryable";
|
||||||
import Checkbox from "../UI/Checkbox";
|
import Checkbox from "../UI/Checkbox";
|
||||||
import ProtobufDecoder from "protobuf-decoder";
|
import ProtobufDecoder from "protobuf-decoder";
|
||||||
|
import {default as jsonBeautify} from "json-beautify";
|
||||||
|
import {default as xmlBeautify} from "xml-formatter";
|
||||||
|
|
||||||
interface EntryViewLineProps {
|
interface EntryViewLineProps {
|
||||||
label: string;
|
label: string;
|
||||||
@ -121,23 +123,41 @@ export const EntryBodySection: React.FC<EntryBodySectionProps> = ({
|
|||||||
contentType,
|
contentType,
|
||||||
selector,
|
selector,
|
||||||
}) => {
|
}) => {
|
||||||
const MAXIMUM_BYTES_TO_HIGHLIGHT = 10000; // The maximum of chars to highlight in body, in case the response can be megabytes
|
const MAXIMUM_BYTES_TO_FORMAT = 1000000; // The maximum of chars to highlight in body, in case the response can be megabytes
|
||||||
const supportedLanguages = [['html', 'html'], ['json', 'json'], ['application/grpc', 'json']]; // [[indicator, languageToUse],...]
|
const jsonLikeFormats = ['json', 'yaml', 'yml'];
|
||||||
const jsonLikeFormats = ['json'];
|
const xmlLikeFormats = ['xml', 'html'];
|
||||||
const protobufFormats = ['application/grpc'];
|
const protobufFormats = ['application/grpc'];
|
||||||
const [isWrapped, setIsWrapped] = useState(false);
|
const supportedFormats = jsonLikeFormats.concat(xmlLikeFormats, protobufFormats);
|
||||||
|
|
||||||
const formatTextBody = (body): string => {
|
const [isPretty, setIsPretty] = useState(true);
|
||||||
const chunk = body.slice(0, MAXIMUM_BYTES_TO_HIGHLIGHT);
|
const [showLineNumbers, setShowLineNumbers] = useState(true);
|
||||||
const bodyBuf = encoding === 'base64' ? atob(chunk) : chunk;
|
const [decodeBase64, setDecodeBase64] = useState(true);
|
||||||
|
|
||||||
|
const isBase64Encoding = encoding === 'base64';
|
||||||
|
const supportsPrettying = supportedFormats.some(format => contentType?.indexOf(format) > -1);
|
||||||
|
|
||||||
|
const formatTextBody = (body: any): string => {
|
||||||
|
if (!decodeBase64) return body;
|
||||||
|
|
||||||
|
const chunk = body.slice(0, MAXIMUM_BYTES_TO_FORMAT);
|
||||||
|
const bodyBuf = isBase64Encoding ? atob(chunk) : chunk;
|
||||||
|
|
||||||
|
if (!isPretty) return bodyBuf;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (jsonLikeFormats.some(format => contentType?.indexOf(format) > -1)) {
|
if (jsonLikeFormats.some(format => contentType?.indexOf(format) > -1)) {
|
||||||
return JSON.stringify(JSON.parse(bodyBuf), null, 2);
|
return jsonBeautify(JSON.parse(bodyBuf), null, 2, 80);
|
||||||
|
} else if (xmlLikeFormats.some(format => contentType?.indexOf(format) > -1)) {
|
||||||
|
return xmlBeautify(bodyBuf, {
|
||||||
|
indentation: ' ',
|
||||||
|
filter: (node) => node.type !== 'Comment',
|
||||||
|
collapseContent: true,
|
||||||
|
lineSeparator: '\n'
|
||||||
|
});
|
||||||
} else if (protobufFormats.some(format => contentType?.indexOf(format) > -1)) {
|
} else if (protobufFormats.some(format => contentType?.indexOf(format) > -1)) {
|
||||||
// Replace all non printable characters (ASCII)
|
// Replace all non printable characters (ASCII)
|
||||||
const protobufDecoder = new ProtobufDecoder(bodyBuf, true);
|
const protobufDecoder = new ProtobufDecoder(bodyBuf, true);
|
||||||
return JSON.stringify(protobufDecoder.decode().toSimple(), null, 2);
|
return jsonBeautify(protobufDecoder.decode().toSimple(), null, 2, 80);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
@ -145,13 +165,6 @@ export const EntryBodySection: React.FC<EntryBodySectionProps> = ({
|
|||||||
return bodyBuf;
|
return bodyBuf;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getLanguage = (mimetype) => {
|
|
||||||
const chunk = content?.slice(0, 100);
|
|
||||||
if (chunk.indexOf('html') > 0 || chunk.indexOf('HTML') > 0) return supportedLanguages[0][1];
|
|
||||||
const language = supportedLanguages.find(el => (mimetype + contentType).indexOf(el[0]) > -1);
|
|
||||||
return language ? language[1] : 'default';
|
|
||||||
}
|
|
||||||
|
|
||||||
return <React.Fragment>
|
return <React.Fragment>
|
||||||
{content && content?.length > 0 && <EntrySectionContainer
|
{content && content?.length > 0 && <EntrySectionContainer
|
||||||
title='Body'
|
title='Body'
|
||||||
@ -159,24 +172,26 @@ export const EntryBodySection: React.FC<EntryBodySectionProps> = ({
|
|||||||
query={`${selector} == r".*"`}
|
query={`${selector} == r".*"`}
|
||||||
updateQuery={updateQuery}
|
updateQuery={updateQuery}
|
||||||
>
|
>
|
||||||
<table>
|
<div style={{display: 'flex', alignItems: 'center', alignContent: 'center', margin: "5px 0"}}>
|
||||||
<tbody>
|
{supportsPrettying && <div style={{paddingTop: 3}}>
|
||||||
<EntryViewLine label={'Mime type'} value={contentType} useTooltip={false}/>
|
<Checkbox checked={isPretty} onToggle={() => {setIsPretty(!isPretty)}}/>
|
||||||
{encoding && <EntryViewLine label={'Encoding'} value={encoding} useTooltip={false}/>}
|
</div>}
|
||||||
</tbody>
|
{supportsPrettying && <span style={{marginLeft: '.2rem'}}>Pretty</span>}
|
||||||
</table>
|
|
||||||
|
|
||||||
<div style={{display: 'flex', alignItems: 'center', alignContent: 'center', margin: "5px 0"}} onClick={() => setIsWrapped(!isWrapped)}>
|
<div style={{paddingTop: 3, paddingLeft: supportsPrettying ? 20 : 0}}>
|
||||||
<div style={{paddingTop: 3}}>
|
<Checkbox checked={showLineNumbers} onToggle={() => {setShowLineNumbers(!showLineNumbers)}}/>
|
||||||
<Checkbox checked={isWrapped} onToggle={() => {}}/>
|
|
||||||
</div>
|
</div>
|
||||||
<span style={{marginLeft: '.5rem'}}>Wrap text</span>
|
<span style={{marginLeft: '.2rem'}}>Line numbers</span>
|
||||||
|
|
||||||
|
{isBase64Encoding && <div style={{paddingTop: 3, paddingLeft: 20}}>
|
||||||
|
<Checkbox checked={decodeBase64} onToggle={() => {setDecodeBase64(!decodeBase64)}}/>
|
||||||
|
</div>}
|
||||||
|
{isBase64Encoding && <span style={{marginLeft: '.2rem'}}>Decode Base64</span>}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
isWrapped={isWrapped}
|
|
||||||
code={formatTextBody(content)}
|
code={formatTextBody(content)}
|
||||||
language={content?.mimeType ? getLanguage(content.mimeType) : 'default'}
|
showLineNumbers={showLineNumbers}
|
||||||
/>
|
/>
|
||||||
</EntrySectionContainer>}
|
</EntrySectionContainer>}
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
@ -334,7 +349,6 @@ export const EntryContractSection: React.FC<EntryContractSectionProps> = ({color
|
|||||||
</EntrySectionContainer>}
|
</EntrySectionContainer>}
|
||||||
{contractContent && <EntrySectionContainer title="Contract" color={color}>
|
{contractContent && <EntrySectionContainer title="Contract" color={color}>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
isWrapped={false}
|
|
||||||
code={contractContent}
|
code={contractContent}
|
||||||
language={"yaml"}
|
language={"yaml"}
|
||||||
/>
|
/>
|
||||||
|
@ -167,7 +167,6 @@ export const QueryForm: React.FC<QueryFormProps> = ({query, setQuery, background
|
|||||||
This is a simple query that matches to HTTP packets with request path "/catalogue":
|
This is a simple query that matches to HTTP packets with request path "/catalogue":
|
||||||
</Typography>
|
</Typography>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
isWrapped={false}
|
|
||||||
showLineNumbers={false}
|
showLineNumbers={false}
|
||||||
code={`http and request.path == "/catalogue"`}
|
code={`http and request.path == "/catalogue"`}
|
||||||
language="python"
|
language="python"
|
||||||
@ -176,7 +175,6 @@ export const QueryForm: React.FC<QueryFormProps> = ({query, setQuery, background
|
|||||||
The same query can be negated for HTTP path and written like this:
|
The same query can be negated for HTTP path and written like this:
|
||||||
</Typography>
|
</Typography>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
isWrapped={false}
|
|
||||||
showLineNumbers={false}
|
showLineNumbers={false}
|
||||||
code={`http and request.path != "/catalogue"`}
|
code={`http and request.path != "/catalogue"`}
|
||||||
language="python"
|
language="python"
|
||||||
@ -185,7 +183,6 @@ export const QueryForm: React.FC<QueryFormProps> = ({query, setQuery, background
|
|||||||
The syntax supports regular expressions. Here is a query that matches the HTTP requests that send JSON to a server:
|
The syntax supports regular expressions. Here is a query that matches the HTTP requests that send JSON to a server:
|
||||||
</Typography>
|
</Typography>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
isWrapped={false}
|
|
||||||
showLineNumbers={false}
|
showLineNumbers={false}
|
||||||
code={`http and request.headers["Accept"] == r"application/json.*"`}
|
code={`http and request.headers["Accept"] == r"application/json.*"`}
|
||||||
language="python"
|
language="python"
|
||||||
@ -194,7 +191,6 @@ export const QueryForm: React.FC<QueryFormProps> = ({query, setQuery, background
|
|||||||
Here is another query that matches HTTP responses with status code 4xx:
|
Here is another query that matches HTTP responses with status code 4xx:
|
||||||
</Typography>
|
</Typography>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
isWrapped={false}
|
|
||||||
showLineNumbers={false}
|
showLineNumbers={false}
|
||||||
code={`http and response.status == r"4.*"`}
|
code={`http and response.status == r"4.*"`}
|
||||||
language="python"
|
language="python"
|
||||||
@ -203,7 +199,6 @@ export const QueryForm: React.FC<QueryFormProps> = ({query, setQuery, background
|
|||||||
The same exact query can be as integer comparison:
|
The same exact query can be as integer comparison:
|
||||||
</Typography>
|
</Typography>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
isWrapped={false}
|
|
||||||
showLineNumbers={false}
|
showLineNumbers={false}
|
||||||
code={`http and response.status >= 400`}
|
code={`http and response.status >= 400`}
|
||||||
language="python"
|
language="python"
|
||||||
@ -212,7 +207,6 @@ export const QueryForm: React.FC<QueryFormProps> = ({query, setQuery, background
|
|||||||
The results can be queried based on their timestamps:
|
The results can be queried based on their timestamps:
|
||||||
</Typography>
|
</Typography>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
isWrapped={false}
|
|
||||||
showLineNumbers={false}
|
showLineNumbers={false}
|
||||||
code={`timestamp < datetime("10/28/2021, 9:13:02.905 PM")`}
|
code={`timestamp < datetime("10/28/2021, 9:13:02.905 PM")`}
|
||||||
language="python"
|
language="python"
|
||||||
@ -224,7 +218,6 @@ export const QueryForm: React.FC<QueryFormProps> = ({query, setQuery, background
|
|||||||
Since Mizu supports various protocols like gRPC, AMQP, Kafka and Redis. It's possible to write complex queries that match multiple protocols like this:
|
Since Mizu supports various protocols like gRPC, AMQP, Kafka and Redis. It's possible to write complex queries that match multiple protocols like this:
|
||||||
</Typography>
|
</Typography>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
isWrapped={false}
|
|
||||||
showLineNumbers={false}
|
showLineNumbers={false}
|
||||||
code={`(http and request.method == "PUT") or (amqp and request.queue.startsWith("test"))\n or (kafka and response.payload.errorCode == 2) or (redis and request.key == "example")\n or (grpc and request.headers[":path"] == r".*foo.*")`}
|
code={`(http and request.method == "PUT") or (amqp and request.queue.startsWith("test"))\n or (kafka and response.payload.errorCode == 2) or (redis and request.key == "example")\n or (grpc and request.headers[":path"] == r".*foo.*")`}
|
||||||
language="python"
|
language="python"
|
||||||
@ -242,7 +235,6 @@ export const QueryForm: React.FC<QueryFormProps> = ({query, setQuery, background
|
|||||||
Such that; clicking this icon in left-pane, would append the query below:
|
Such that; clicking this icon in left-pane, would append the query below:
|
||||||
</Typography>
|
</Typography>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
isWrapped={false}
|
|
||||||
showLineNumbers={false}
|
showLineNumbers={false}
|
||||||
code={`and dst.name == "carts.sock-shop"`}
|
code={`and dst.name == "carts.sock-shop"`}
|
||||||
language="python"
|
language="python"
|
||||||
@ -260,7 +252,6 @@ export const QueryForm: React.FC<QueryFormProps> = ({query, setQuery, background
|
|||||||
A query that compares one selector to another is also a valid query:
|
A query that compares one selector to another is also a valid query:
|
||||||
</Typography>
|
</Typography>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
isWrapped={false}
|
|
||||||
showLineNumbers={false}
|
showLineNumbers={false}
|
||||||
code={`http and (request.query["x"] == response.headers["y"]\n or response.content.text.contains(request.query["x"]))`}
|
code={`http and (request.query["x"] == response.headers["y"]\n or response.content.text.contains(request.query["x"]))`}
|
||||||
language="python"
|
language="python"
|
||||||
@ -276,7 +267,6 @@ export const QueryForm: React.FC<QueryFormProps> = ({query, setQuery, background
|
|||||||
true if the given selector's value starts with the string:
|
true if the given selector's value starts with the string:
|
||||||
</Typography>
|
</Typography>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
isWrapped={false}
|
|
||||||
showLineNumbers={false}
|
showLineNumbers={false}
|
||||||
code={`request.path.startsWith("something")`}
|
code={`request.path.startsWith("something")`}
|
||||||
language="python"
|
language="python"
|
||||||
@ -285,7 +275,6 @@ export const QueryForm: React.FC<QueryFormProps> = ({query, setQuery, background
|
|||||||
true if the given selector's value ends with the string:
|
true if the given selector's value ends with the string:
|
||||||
</Typography>
|
</Typography>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
isWrapped={false}
|
|
||||||
showLineNumbers={false}
|
showLineNumbers={false}
|
||||||
code={`request.path.endsWith("something")`}
|
code={`request.path.endsWith("something")`}
|
||||||
language="python"
|
language="python"
|
||||||
@ -294,7 +283,6 @@ export const QueryForm: React.FC<QueryFormProps> = ({query, setQuery, background
|
|||||||
true if the given selector's value contains the string:
|
true if the given selector's value contains the string:
|
||||||
</Typography>
|
</Typography>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
isWrapped={false}
|
|
||||||
showLineNumbers={false}
|
showLineNumbers={false}
|
||||||
code={`request.path.contains("something")`}
|
code={`request.path.contains("something")`}
|
||||||
language="python"
|
language="python"
|
||||||
@ -303,7 +291,6 @@ export const QueryForm: React.FC<QueryFormProps> = ({query, setQuery, background
|
|||||||
returns the UNIX timestamp which is the equivalent of the time that's provided by the string. Invalid input evaluates to false:
|
returns the UNIX timestamp which is the equivalent of the time that's provided by the string. Invalid input evaluates to false:
|
||||||
</Typography>
|
</Typography>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
isWrapped={false}
|
|
||||||
showLineNumbers={false}
|
showLineNumbers={false}
|
||||||
code={`timestamp >= datetime("10/19/2021, 6:29:02.593 PM")`}
|
code={`timestamp >= datetime("10/19/2021, 6:29:02.593 PM")`}
|
||||||
language="python"
|
language="python"
|
||||||
@ -312,7 +299,6 @@ export const QueryForm: React.FC<QueryFormProps> = ({query, setQuery, background
|
|||||||
limits the number of records that are streamed back as a result of a query. Always evaluates to true:
|
limits the number of records that are streamed back as a result of a query. Always evaluates to true:
|
||||||
</Typography>
|
</Typography>
|
||||||
<SyntaxHighlighter
|
<SyntaxHighlighter
|
||||||
isWrapped={false}
|
|
||||||
showLineNumbers={false}
|
showLineNumbers={false}
|
||||||
code={`and limit(100)`}
|
code={`and limit(100)`}
|
||||||
language="python"
|
language="python"
|
||||||
|
@ -1,152 +0,0 @@
|
|||||||
export const highlighterStyle = {
|
|
||||||
"code[class*=\"language-\"]": {
|
|
||||||
"color": "#494677",
|
|
||||||
"fontFamily": "Inconsolata, Monaco, Consolas, 'Courier New', Courier, monospace",
|
|
||||||
"direction": "ltr",
|
|
||||||
"textAlign": "left",
|
|
||||||
"whiteSpace": "pre",
|
|
||||||
"wordSpacing": "normal",
|
|
||||||
"wordBreak": "normal",
|
|
||||||
"lineHeight": "1.5",
|
|
||||||
"MozTabSize": "4",
|
|
||||||
"OTabSize": "4",
|
|
||||||
"tabSize": "4",
|
|
||||||
"padding": "1rem",
|
|
||||||
"WebkitHyphetokenns": "none",
|
|
||||||
"MozHyphens": "none",
|
|
||||||
"msHyphens": "none",
|
|
||||||
"hyphens": "none"
|
|
||||||
},
|
|
||||||
"pre[class*=\"language-\"]": {
|
|
||||||
"color": "#494677",
|
|
||||||
"fontFamily": "Inconsolata, Monaco, Consolas, 'Courier New', Courier, monospace",
|
|
||||||
"direction": "ltr",
|
|
||||||
"textAlign": "left",
|
|
||||||
"whiteSpace": "pre",
|
|
||||||
"wordSpacing": "normal",
|
|
||||||
"wordBreak": "normal",
|
|
||||||
"lineHeight": "1.2",
|
|
||||||
"MozTabSize": "4",
|
|
||||||
"OTabSize": "4",
|
|
||||||
"tabSize": "4",
|
|
||||||
"WebkitHyphens": "none",
|
|
||||||
"MozHyphens": "none",
|
|
||||||
"msHyphens": "none",
|
|
||||||
"hyphens": "none",
|
|
||||||
"padding": "0",
|
|
||||||
"margin": ".5em 0",
|
|
||||||
"overflow": "auto",
|
|
||||||
"borderRadius": "0.3em",
|
|
||||||
"background": "#F7F9FC"
|
|
||||||
},
|
|
||||||
":not(pre) > code[class*=\"language-\"]": {
|
|
||||||
"background": "#F7F9FC",
|
|
||||||
"padding": ".1em",
|
|
||||||
"borderRadius": ".3em"
|
|
||||||
},
|
|
||||||
"comment": {
|
|
||||||
"color": "#5d6aa0"
|
|
||||||
},
|
|
||||||
"prolog": {
|
|
||||||
"color": "#494677"
|
|
||||||
},
|
|
||||||
"doctype": {
|
|
||||||
"color": "#494677"
|
|
||||||
},
|
|
||||||
"cdata": {
|
|
||||||
"color": "#494677"
|
|
||||||
},
|
|
||||||
"punctuation": {
|
|
||||||
"color": "#494677"
|
|
||||||
},
|
|
||||||
".namespace": {
|
|
||||||
"Opacity": ".7"
|
|
||||||
},
|
|
||||||
"property": {
|
|
||||||
"color": "#627ef7"
|
|
||||||
},
|
|
||||||
"keyword": {
|
|
||||||
"color": "#627ef7"
|
|
||||||
},
|
|
||||||
"tag": {
|
|
||||||
"color": "#627ef7"
|
|
||||||
},
|
|
||||||
"class-name": {
|
|
||||||
"color": "#3eb545",
|
|
||||||
"textDecoration": "underline"
|
|
||||||
},
|
|
||||||
"boolean": {
|
|
||||||
"color": "#3eb545"
|
|
||||||
},
|
|
||||||
"constant": {
|
|
||||||
"color": "#3eb545"
|
|
||||||
},
|
|
||||||
"symbol": {
|
|
||||||
"color": "#ff3a30"
|
|
||||||
},
|
|
||||||
"deleted": {
|
|
||||||
"color": "#ff3a30"
|
|
||||||
},
|
|
||||||
"number": {
|
|
||||||
"color": "#ff16f7"
|
|
||||||
},
|
|
||||||
"selector": {
|
|
||||||
"color": "rgb(9,224,19)"
|
|
||||||
},
|
|
||||||
"attr-name": {
|
|
||||||
"color": "rgb(9,224,19)"
|
|
||||||
},
|
|
||||||
"string": {
|
|
||||||
"color": "rgb(9,224,19)"
|
|
||||||
},
|
|
||||||
"char": {
|
|
||||||
"color": "rgb(9,224,19)"
|
|
||||||
},
|
|
||||||
"builtin": {
|
|
||||||
"color": "rgb(9,224,19)"
|
|
||||||
},
|
|
||||||
"inserted": {
|
|
||||||
"color": "rgb(9,224,19)"
|
|
||||||
},
|
|
||||||
"variable": {
|
|
||||||
"color": "#C6C5FE"
|
|
||||||
},
|
|
||||||
"operator": {
|
|
||||||
"color": "#A1A1A1"
|
|
||||||
},
|
|
||||||
"entity": {
|
|
||||||
"color": "#fdab2b",
|
|
||||||
"cursor": "help"
|
|
||||||
},
|
|
||||||
"url": {
|
|
||||||
"color": "#96CBFE"
|
|
||||||
},
|
|
||||||
".language-css .token.string": {
|
|
||||||
"color": "#87C38A"
|
|
||||||
},
|
|
||||||
".style .token.string": {
|
|
||||||
"color": "#87C38A"
|
|
||||||
},
|
|
||||||
"atrule": {
|
|
||||||
"color": "#fdab2b"
|
|
||||||
},
|
|
||||||
"attr-value": {
|
|
||||||
"color": "#f8c575"
|
|
||||||
},
|
|
||||||
"function": {
|
|
||||||
"color": "#fdab2b"
|
|
||||||
},
|
|
||||||
"regex": {
|
|
||||||
"color": "#fab248"
|
|
||||||
},
|
|
||||||
"important": {
|
|
||||||
"color": "#fd971f",
|
|
||||||
"fontWeight": "bold"
|
|
||||||
},
|
|
||||||
"bold": {
|
|
||||||
"fontWeight": "bold"
|
|
||||||
},
|
|
||||||
"italic": {
|
|
||||||
"fontStyle": "italic"
|
|
||||||
}
|
|
||||||
};
|
|
@ -26,12 +26,24 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.wrapped{
|
code.hljs {
|
||||||
pre {
|
white-space: pre-wrap;
|
||||||
code {
|
}
|
||||||
&:last-child {
|
|
||||||
white-space: pre-wrap!important
|
code.hljs:before {
|
||||||
}
|
counter-reset: listing;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
code.hljs .hljs-marker-line {
|
||||||
|
counter-increment: listing;
|
||||||
|
}
|
||||||
|
|
||||||
|
code.hljs .hljs-marker-line:before {
|
||||||
|
content: counter(listing) " ";
|
||||||
|
display: inline-block;
|
||||||
|
width: 3rem;
|
||||||
|
padding-left: auto;
|
||||||
|
margin-left: auto;
|
||||||
|
text-align: right;
|
||||||
|
opacity: .5;
|
||||||
}
|
}
|
@ -1,30 +1,47 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {Prism as SyntaxHighlighterContainer} from 'react-syntax-highlighter';
|
import Lowlight from 'react-lowlight'
|
||||||
import {highlighterStyle} from './highlighterStyle'
|
import 'highlight.js/styles/atom-one-light.css'
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
|
import xml from 'highlight.js/lib/languages/xml'
|
||||||
|
import json from 'highlight.js/lib/languages/json'
|
||||||
|
import protobuf from 'highlight.js/lib/languages/protobuf'
|
||||||
|
import javascript from 'highlight.js/lib/languages/javascript'
|
||||||
|
import actionscript from 'highlight.js/lib/languages/actionscript'
|
||||||
|
import wasm from 'highlight.js/lib/languages/wasm'
|
||||||
|
import handlebars from 'highlight.js/lib/languages/handlebars'
|
||||||
|
import yaml from 'highlight.js/lib/languages/yaml'
|
||||||
|
import python from 'highlight.js/lib/languages/python'
|
||||||
|
|
||||||
|
Lowlight.registerLanguage('python', python);
|
||||||
|
Lowlight.registerLanguage('xml', xml);
|
||||||
|
Lowlight.registerLanguage('json', json);
|
||||||
|
Lowlight.registerLanguage('yaml', yaml);
|
||||||
|
Lowlight.registerLanguage('protobuf', protobuf);
|
||||||
|
Lowlight.registerLanguage('javascript', javascript);
|
||||||
|
Lowlight.registerLanguage('actionscript', actionscript);
|
||||||
|
Lowlight.registerLanguage('wasm', wasm);
|
||||||
|
Lowlight.registerLanguage('handlebars', handlebars);
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
code: string;
|
code: string;
|
||||||
style?: any;
|
|
||||||
showLineNumbers?: boolean;
|
showLineNumbers?: boolean;
|
||||||
className?: string;
|
|
||||||
language?: string;
|
language?: string;
|
||||||
isWrapped?: boolean;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const SyntaxHighlighter: React.FC<Props> = ({
|
export const SyntaxHighlighter: React.FC<Props> = ({
|
||||||
code,
|
code,
|
||||||
style = highlighterStyle,
|
showLineNumbers = false,
|
||||||
showLineNumbers = true,
|
language = null
|
||||||
className,
|
|
||||||
language = 'python',
|
|
||||||
isWrapped = false,
|
|
||||||
}) => {
|
}) => {
|
||||||
return <div className={`highlighterContainer ${className ? className : ''} ${isWrapped ? 'wrapped' : ''}`}>
|
const markers = showLineNumbers ? code.split("\n").map((item, i) => {
|
||||||
<SyntaxHighlighterContainer language={language} style={style} showLineNumbers={showLineNumbers}>
|
return {
|
||||||
{code ?? ""}
|
line: i + 1,
|
||||||
</SyntaxHighlighterContainer>
|
className: 'hljs-marker-line'
|
||||||
</div>;
|
}
|
||||||
|
}) : [];
|
||||||
|
|
||||||
|
return <div style={{fontSize: ".75rem"}}><Lowlight language={language ? language : ""} value={code} markers={markers}/></div>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SyntaxHighlighter;
|
export default SyntaxHighlighter;
|
||||||
|
Loading…
Reference in New Issue
Block a user