From ae91b2ae9c3507630cbd390d670b6fa700c86c89 Mon Sep 17 00:00:00 2001 From: Edgar Fisher <fisher.edgar@gmail.com> Date: Wed, 19 Mar 2025 10:07:45 +0200 Subject: [PATCH 1/3] feat: Add correlation label --- src/components/TextWithTooltip.tsx | 16 +++ src/components/WebLogView/Row.tsx | 136 +++++++++++------- src/components/WebLogView/SearchResults.tsx | 4 +- src/rules/correlation.ts | 4 - .../Generator/GeneratorTabs/RequestList.tsx | 4 +- .../Generator/GeneratorTabs/RequestRow.tsx | 136 ++++++++++++++---- .../Generator/GeneratorTabs/RequestTable.tsx | 8 +- 7 files changed, 211 insertions(+), 97 deletions(-) create mode 100644 src/components/TextWithTooltip.tsx diff --git a/src/components/TextWithTooltip.tsx b/src/components/TextWithTooltip.tsx new file mode 100644 index 00000000..483c90d3 --- /dev/null +++ b/src/components/TextWithTooltip.tsx @@ -0,0 +1,16 @@ +import { useOverflowCheck } from '@/hooks/useOverflowCheck' +import { Tooltip, Text, TextProps } from '@radix-ui/themes' +import { useRef } from 'react' + +export function TextWithTooltip({ children, ...props }: TextProps) { + const ref = useRef<HTMLElement>(null) + const isEllipsisActive = useOverflowCheck(ref) + + return ( + <Tooltip content={children} hidden={!isEllipsisActive} avoidCollisions> + <Text {...props} truncate ref={ref}> + {children} + </Text> + </Tooltip> + ) +} diff --git a/src/components/WebLogView/Row.tsx b/src/components/WebLogView/Row.tsx index 5b6179f9..ec7c5d6c 100644 --- a/src/components/WebLogView/Row.tsx +++ b/src/components/WebLogView/Row.tsx @@ -14,30 +14,22 @@ export interface RowProps { isSelected?: boolean onSelectRequest: (data: ProxyDataWithMatches) => void filter?: string - className?: string } -export function Row({ - data, - onSelectRequest, - isSelected, - filter, - className, -}: RowProps) { +export function Row({ data, onSelectRequest, isSelected, filter }: RowProps) { return ( <> - <Table.Row - onClick={() => onSelectRequest(data)} - className={className} - css={{ - backgroundColor: isSelected ? 'var(--accent-3)' : 'transparent', - '&:hover': { - backgroundColor: isSelected ? 'var(--accent-3)' : 'var(--accent-2)', - }, - }} + <TableRow + data={data} + onSelectRequest={onSelectRequest} + isSelected={isSelected} > - <Cells data={data} isSelected={isSelected} /> - </Table.Row> + <MethodCell data={data} isSelected={isSelected} /> + <StatusCell data={data} /> + <RequestTypeCell data={data} /> + <HostCell data={data} /> + <PathCell data={data} /> + </TableRow> <SearchResults data={data} @@ -49,7 +41,28 @@ export function Row({ ) } -export function Cells({ +export function TableRow({ + data, + onSelectRequest, + isSelected, + children, +}: RowProps & { children: React.ReactNode }) { + return ( + <Table.Row + onClick={() => onSelectRequest(data)} + css={{ + backgroundColor: isSelected ? 'var(--accent-3)' : 'transparent', + '&:hover': { + backgroundColor: isSelected ? 'var(--accent-3)' : 'var(--accent-2)', + }, + }} + > + {children} + </Table.Row> + ) +} + +export function MethodCell({ data, isSelected, }: { @@ -57,43 +70,58 @@ export function Cells({ isSelected?: boolean }) { return ( - <> - <Table.Cell + <Table.Cell + css={{ + cursor: 'var(--cursor-button)', + padding: '0', + display: 'flex', + alignItems: 'center', + }} + > + <Box css={{ - cursor: 'var(--cursor-button)', - padding: '0', - display: 'flex', - alignItems: 'center', + width: '3px', + backgroundColor: isSelected ? 'var(--accent-9)' : 'transparent', + height: 'var(--table-cell-min-height)', + marginRight: 'var(--space-2)', }} - > - <Box - css={{ - width: '3px', - backgroundColor: isSelected ? 'var(--accent-9)' : 'transparent', - height: 'var(--table-cell-min-height)', - marginRight: 'var(--space-2)', - }} + /> + <MethodBadge method={data.request.method}> + <HighlightedText text={data.request.method} matches={data.matches} /> + </MethodBadge> + </Table.Cell> + ) +} + +export function StatusCell({ data }: { data: ProxyDataWithMatches }) { + return ( + <Table.Cell> + <ResponseStatusBadge status={data.response?.statusCode}> + <HighlightedText + text={data.response?.statusCode.toString() ?? '-'} + matches={data.matches} /> - <MethodBadge method={data.request.method}> - <HighlightedText text={data.request.method} matches={data.matches} /> - </MethodBadge> - </Table.Cell> + </ResponseStatusBadge> + </Table.Cell> + ) +} - <Table.Cell> - <ResponseStatusBadge status={data.response?.statusCode}> - <HighlightedText - text={data.response?.statusCode.toString() ?? '-'} - matches={data.matches} - /> - </ResponseStatusBadge> - </Table.Cell> - <TableCellWithTooltip>{getRequestType(data)}</TableCellWithTooltip> - <TableCellWithTooltip> - <HighlightedText text={data.request.host} matches={data.matches} /> - </TableCellWithTooltip> - <TableCellWithTooltip> - <HighlightedText text={data.request.path} matches={data.matches} /> - </TableCellWithTooltip> - </> +export function RequestTypeCell({ data }: { data: ProxyDataWithMatches }) { + return <TableCellWithTooltip>{getRequestType(data)}</TableCellWithTooltip> +} + +export function HostCell({ data }: { data: ProxyDataWithMatches }) { + return ( + <TableCellWithTooltip> + <HighlightedText text={data.request.host} matches={data.matches} /> + </TableCellWithTooltip> + ) +} + +export function PathCell({ data }: { data: ProxyDataWithMatches }) { + return ( + <TableCellWithTooltip> + <HighlightedText text={data.request.path} matches={data.matches} /> + </TableCellWithTooltip> ) } diff --git a/src/components/WebLogView/SearchResults.tsx b/src/components/WebLogView/SearchResults.tsx index 1c58aace..c04d234b 100644 --- a/src/components/WebLogView/SearchResults.tsx +++ b/src/components/WebLogView/SearchResults.tsx @@ -39,12 +39,10 @@ export function SearchResults({ data, onSelectRequest, filter, - colSpan = 5, }: { data: ProxyDataWithMatches onSelectRequest: (data: ProxyDataWithMatches) => void filter?: string - colSpan?: number }) { const { setTab: setRequestTab } = useRequestDetailsTab() const { setTab: setResponseTab } = useResponseDetailsTab() @@ -105,7 +103,7 @@ export function SearchResults({ return ( <Table.Row> - <Table.Cell colSpan={colSpan}> + <Table.Cell colSpan={5}> {visibleResults.map((result, excerptIndex) => ( <Flex key={excerptIndex} diff --git a/src/rules/correlation.ts b/src/rules/correlation.ts index 06f09ac3..0c1e3a8a 100644 --- a/src/rules/correlation.ts +++ b/src/rules/correlation.ts @@ -143,10 +143,6 @@ function applyRule({ ], count: state.count + 1, - matchedRequestIds: [ - ...state.matchedRequestIds, - requestSnippetSchema.data.id, - ], }) return { diff --git a/src/views/Generator/GeneratorTabs/RequestList.tsx b/src/views/Generator/GeneratorTabs/RequestList.tsx index 3b83a8e7..26dd48da 100644 --- a/src/views/Generator/GeneratorTabs/RequestList.tsx +++ b/src/views/Generator/GeneratorTabs/RequestList.tsx @@ -98,9 +98,7 @@ export function RequestList({ ListComponent={(props) => ( <RequestTable {...props} - highlightedRequestIds={ - selectedRuleInstance?.state?.matchedRequestIds - } + selectedRuleInstance={selectedRuleInstance} /> )} /> diff --git a/src/views/Generator/GeneratorTabs/RequestRow.tsx b/src/views/Generator/GeneratorTabs/RequestRow.tsx index 536e6125..d11a90b2 100644 --- a/src/views/Generator/GeneratorTabs/RequestRow.tsx +++ b/src/views/Generator/GeneratorTabs/RequestRow.tsx @@ -1,6 +1,18 @@ -import { Cells, RowProps } from '@/components/WebLogView' +import { HighlightedText } from '@/components/HighlightedText' +import { Table } from '@/components/Table' +import { TextWithTooltip } from '@/components/TextWithTooltip' +import { + HostCell, + MethodCell, + RequestTypeCell, + RowProps, + StatusCell, + TableRow, +} from '@/components/WebLogView' import { SearchResults } from '@/components/WebLogView/SearchResults' -import { Badge, Flex, Strong, Table } from '@radix-ui/themes' +import { ProxyData } from '@/types' +import { RuleInstance } from '@/types/rules' +import { Badge, Flex, Strong } from '@radix-ui/themes' import { useMemo } from 'react' export function RequestRow({ @@ -8,45 +20,111 @@ export function RequestRow({ onSelectRequest, isSelected, filter, - highlightedRequestIds, -}: RowProps & { highlightedRequestIds?: string[] }) { - const isMatch = useMemo(() => { - if (!highlightedRequestIds) { - return false - } - - return highlightedRequestIds.includes(data.id) - }, [highlightedRequestIds, data.id]) + selectedRuleInstance, +}: RowProps & { selectedRuleInstance?: RuleInstance }) { return ( <> - <Table.Row - onClick={() => onSelectRequest(data)} - css={{ - backgroundColor: isSelected ? 'var(--accent-3)' : 'transparent', - '&:hover': { - backgroundColor: isSelected ? 'var(--accent-3)' : 'var(--accent-2)', - }, - }} + <TableRow + data={data} + onSelectRequest={onSelectRequest} + isSelected={isSelected} > - <Cells data={data} isSelected={isSelected} /> + <MethodCell data={data} isSelected={isSelected} /> + <StatusCell data={data} /> + <RequestTypeCell data={data} /> + <HostCell data={data} /> + <Table.Cell css={{ padding: 0 }}> - {isMatch && ( - <Flex justify="end" align="center" height="100%" pr="2"> - <Badge color="green" size="1"> - <Strong>Match</Strong> - </Badge> - </Flex> - )} + <Flex justify="between" align="center" height="100%" gap="1"> + <TextWithTooltip size="1"> + <HighlightedText + text={data.request.path} + matches={data.matches} + /> + </TextWithTooltip> + <RuleBadges + selectedRuleInstance={selectedRuleInstance} + data={data} + /> + </Flex> </Table.Cell> - </Table.Row> + </TableRow> <SearchResults data={data} key={data.id} onSelectRequest={onSelectRequest} filter={filter} - colSpan={6} /> </> ) } + +function RuleBadges({ + selectedRuleInstance, + data, +}: { + selectedRuleInstance?: RuleInstance + data: ProxyData +}) { + if (!selectedRuleInstance) { + return null + } + + return ( + <Flex justify="end" align="center" height="100%" pr="2" gap="2"> + <ExtractorBadge selectedRuleInstance={selectedRuleInstance} data={data} /> + <MatchBadge selectedRuleInstance={selectedRuleInstance} data={data} /> + </Flex> + ) +} + +function MatchBadge({ + selectedRuleInstance, + data, +}: { + selectedRuleInstance: RuleInstance + data: ProxyData +}) { + const isMatch = useMemo(() => { + return selectedRuleInstance.state.matchedRequestIds.includes(data.id) + }, [selectedRuleInstance, data.id]) + + if (!isMatch) { + return null + } + + return ( + <Badge color="green" size="1"> + <Strong>Match</Strong> + </Badge> + ) +} + +function ExtractorBadge({ + selectedRuleInstance, + data, +}: { + selectedRuleInstance: RuleInstance + data: ProxyData +}) { + const isExtractor = useMemo(() => { + if (selectedRuleInstance.type !== 'correlation') { + return false + } + + return selectedRuleInstance.state.responsesExtracted.some( + (request) => request.id === data.id + ) + }, [selectedRuleInstance, data.id]) + + if (!isExtractor) { + return null + } + + return ( + <Badge color="blue" size="1"> + <Strong>Value extracted</Strong> + </Badge> + ) +} diff --git a/src/views/Generator/GeneratorTabs/RequestTable.tsx b/src/views/Generator/GeneratorTabs/RequestTable.tsx index dedeb1b5..e1b6221f 100644 --- a/src/views/Generator/GeneratorTabs/RequestTable.tsx +++ b/src/views/Generator/GeneratorTabs/RequestTable.tsx @@ -2,14 +2,15 @@ import { Table } from '@/components/Table' import { RequestListProps } from '@/components/WebLogView' import { RequestRow } from './RequestRow' +import { RuleInstance } from '@/types/rules' export function RequestTable({ requests, selectedRequestId, onSelectRequest, filter, - highlightedRequestIds, -}: RequestListProps & { highlightedRequestIds?: string[] }) { + selectedRuleInstance, +}: RequestListProps & { selectedRuleInstance?: RuleInstance }) { return ( <Table.Root size="1" layout="fixed"> <Table.Header css={{ textWrap: 'nowrap' }}> @@ -19,7 +20,6 @@ export function RequestTable({ <Table.ColumnHeaderCell width="50px">Type</Table.ColumnHeaderCell> <Table.ColumnHeaderCell width="20%">Host</Table.ColumnHeaderCell> <Table.ColumnHeaderCell width="80%">Path</Table.ColumnHeaderCell> - <Table.ColumnHeaderCell width="55px" /> </Table.Row> </Table.Header> <Table.Body> @@ -30,7 +30,7 @@ export function RequestTable({ isSelected={selectedRequestId === data.id} onSelectRequest={onSelectRequest} filter={filter} - highlightedRequestIds={highlightedRequestIds} + selectedRuleInstance={selectedRuleInstance} /> ))} </Table.Body> From 4edd8aede4eef4098ac7424ce824e9e2a5c505c7 Mon Sep 17 00:00:00 2001 From: Edgar Fisher <fisher.edgar@gmail.com> Date: Wed, 19 Mar 2025 10:16:55 +0200 Subject: [PATCH 2/3] refactor: move request list components into subfolder --- .../Header.tsx} | 2 +- .../{ => RequestList}/RequestList.tsx | 4 +- .../{ => RequestList}/RequestList.utils.tsx | 2 +- .../{ => RequestList}/RequestRow.tsx | 74 +------------------ .../{ => RequestList}/RequestTable.tsx | 0 .../GeneratorTabs/RequestList/RuleBadges.tsx | 73 ++++++++++++++++++ .../GeneratorTabs/RequestList/index.ts | 1 + 7 files changed, 80 insertions(+), 76 deletions(-) rename src/views/Generator/GeneratorTabs/{RequestListHeader.tsx => RequestList/Header.tsx} (98%) rename src/views/Generator/GeneratorTabs/{ => RequestList}/RequestList.tsx (97%) rename src/views/Generator/GeneratorTabs/{ => RequestList}/RequestList.utils.tsx (97%) rename src/views/Generator/GeneratorTabs/{ => RequestList}/RequestRow.tsx (50%) rename src/views/Generator/GeneratorTabs/{ => RequestList}/RequestTable.tsx (100%) create mode 100644 src/views/Generator/GeneratorTabs/RequestList/RuleBadges.tsx create mode 100644 src/views/Generator/GeneratorTabs/RequestList/index.ts diff --git a/src/views/Generator/GeneratorTabs/RequestListHeader.tsx b/src/views/Generator/GeneratorTabs/RequestList/Header.tsx similarity index 98% rename from src/views/Generator/GeneratorTabs/RequestListHeader.tsx rename to src/views/Generator/GeneratorTabs/RequestList/Header.tsx index dedb7a72..3b5208ab 100644 --- a/src/views/Generator/GeneratorTabs/RequestListHeader.tsx +++ b/src/views/Generator/GeneratorTabs/RequestList/Header.tsx @@ -4,7 +4,7 @@ import { useGeneratorStore } from '@/store/generator' import { getFileNameWithoutExtension } from '@/utils/file' import { RecorderIcon } from '@/components/icons' -export function RequestListHeader({ +export function Header({ filter, setFilter, filterAllData, diff --git a/src/views/Generator/GeneratorTabs/RequestList.tsx b/src/views/Generator/GeneratorTabs/RequestList/RequestList.tsx similarity index 97% rename from src/views/Generator/GeneratorTabs/RequestList.tsx rename to src/views/Generator/GeneratorTabs/RequestList/RequestList.tsx index 26dd48da..50d63bf6 100644 --- a/src/views/Generator/GeneratorTabs/RequestList.tsx +++ b/src/views/Generator/GeneratorTabs/RequestList/RequestList.tsx @@ -9,8 +9,8 @@ import { useStudioUIStore } from '@/store/ui' import { useGeneratorStore } from '@/store/generator' import { EmptyMessage } from '@/components/EmptyMessage' import { validateRecording } from './RequestList.utils' -import { RequestListHeader } from './RequestListHeader' import { useApplyRules } from '@/store/hooks/useApplyRules' +import { Header } from './Header' import { RequestTable } from './RequestTable' interface RequestListProps { @@ -71,7 +71,7 @@ export function RequestList({ return ( <Flex direction="column" height="100%"> {!recordingError && ( - <RequestListHeader + <Header filter={filter} setFilter={setFilter} filterAllData={filterAllData} diff --git a/src/views/Generator/GeneratorTabs/RequestList.utils.tsx b/src/views/Generator/GeneratorTabs/RequestList/RequestList.utils.tsx similarity index 97% rename from src/views/Generator/GeneratorTabs/RequestList.utils.tsx rename to src/views/Generator/GeneratorTabs/RequestList/RequestList.utils.tsx index 4abefca8..8d176019 100644 --- a/src/views/Generator/GeneratorTabs/RequestList.utils.tsx +++ b/src/views/Generator/GeneratorTabs/RequestList/RequestList.utils.tsx @@ -3,7 +3,7 @@ import { ProxyData } from '@/types' import { GlobeIcon } from '@radix-ui/react-icons' import { Button } from '@radix-ui/themes' import { ComponentProps } from 'react' -import { RecordingSelector } from '../RecordingSelector' +import { RecordingSelector } from '../../RecordingSelector' export function validateRecording({ allowlist, diff --git a/src/views/Generator/GeneratorTabs/RequestRow.tsx b/src/views/Generator/GeneratorTabs/RequestList/RequestRow.tsx similarity index 50% rename from src/views/Generator/GeneratorTabs/RequestRow.tsx rename to src/views/Generator/GeneratorTabs/RequestList/RequestRow.tsx index d11a90b2..642ce0d3 100644 --- a/src/views/Generator/GeneratorTabs/RequestRow.tsx +++ b/src/views/Generator/GeneratorTabs/RequestList/RequestRow.tsx @@ -10,10 +10,9 @@ import { TableRow, } from '@/components/WebLogView' import { SearchResults } from '@/components/WebLogView/SearchResults' -import { ProxyData } from '@/types' import { RuleInstance } from '@/types/rules' -import { Badge, Flex, Strong } from '@radix-ui/themes' -import { useMemo } from 'react' +import { Flex } from '@radix-ui/themes' +import { RuleBadges } from './RuleBadges' export function RequestRow({ data, @@ -59,72 +58,3 @@ export function RequestRow({ </> ) } - -function RuleBadges({ - selectedRuleInstance, - data, -}: { - selectedRuleInstance?: RuleInstance - data: ProxyData -}) { - if (!selectedRuleInstance) { - return null - } - - return ( - <Flex justify="end" align="center" height="100%" pr="2" gap="2"> - <ExtractorBadge selectedRuleInstance={selectedRuleInstance} data={data} /> - <MatchBadge selectedRuleInstance={selectedRuleInstance} data={data} /> - </Flex> - ) -} - -function MatchBadge({ - selectedRuleInstance, - data, -}: { - selectedRuleInstance: RuleInstance - data: ProxyData -}) { - const isMatch = useMemo(() => { - return selectedRuleInstance.state.matchedRequestIds.includes(data.id) - }, [selectedRuleInstance, data.id]) - - if (!isMatch) { - return null - } - - return ( - <Badge color="green" size="1"> - <Strong>Match</Strong> - </Badge> - ) -} - -function ExtractorBadge({ - selectedRuleInstance, - data, -}: { - selectedRuleInstance: RuleInstance - data: ProxyData -}) { - const isExtractor = useMemo(() => { - if (selectedRuleInstance.type !== 'correlation') { - return false - } - - return selectedRuleInstance.state.responsesExtracted.some( - (request) => request.id === data.id - ) - }, [selectedRuleInstance, data.id]) - - if (!isExtractor) { - return null - } - - return ( - <Badge color="blue" size="1"> - <Strong>Value extracted</Strong> - </Badge> - ) -} diff --git a/src/views/Generator/GeneratorTabs/RequestTable.tsx b/src/views/Generator/GeneratorTabs/RequestList/RequestTable.tsx similarity index 100% rename from src/views/Generator/GeneratorTabs/RequestTable.tsx rename to src/views/Generator/GeneratorTabs/RequestList/RequestTable.tsx diff --git a/src/views/Generator/GeneratorTabs/RequestList/RuleBadges.tsx b/src/views/Generator/GeneratorTabs/RequestList/RuleBadges.tsx new file mode 100644 index 00000000..23754243 --- /dev/null +++ b/src/views/Generator/GeneratorTabs/RequestList/RuleBadges.tsx @@ -0,0 +1,73 @@ +import { ProxyData } from '@/types' +import { RuleInstance } from '@/types/rules' +import { Badge, Flex, Strong } from '@radix-ui/themes' +import { useMemo } from 'react' + +export function RuleBadges({ + selectedRuleInstance, + data, +}: { + selectedRuleInstance?: RuleInstance + data: ProxyData +}) { + if (!selectedRuleInstance) { + return null + } + + return ( + <Flex justify="end" align="center" height="100%" pr="2" gap="2"> + <ExtractorBadge selectedRuleInstance={selectedRuleInstance} data={data} /> + <MatchBadge selectedRuleInstance={selectedRuleInstance} data={data} /> + </Flex> + ) +} + +function MatchBadge({ + selectedRuleInstance, + data, +}: { + selectedRuleInstance: RuleInstance + data: ProxyData +}) { + const isMatch = useMemo(() => { + return selectedRuleInstance.state.matchedRequestIds.includes(data.id) + }, [selectedRuleInstance, data.id]) + + if (!isMatch) { + return null + } + + return ( + <Badge color="green" size="1"> + <Strong>Match</Strong> + </Badge> + ) +} + +function ExtractorBadge({ + selectedRuleInstance, + data, +}: { + selectedRuleInstance: RuleInstance + data: ProxyData +}) { + const isExtractor = useMemo(() => { + if (selectedRuleInstance.type !== 'correlation') { + return false + } + + return selectedRuleInstance.state.responsesExtracted.some( + (request) => request.id === data.id + ) + }, [selectedRuleInstance, data.id]) + + if (!isExtractor) { + return null + } + + return ( + <Badge color="blue" size="1"> + <Strong>Value extracted</Strong> + </Badge> + ) +} diff --git a/src/views/Generator/GeneratorTabs/RequestList/index.ts b/src/views/Generator/GeneratorTabs/RequestList/index.ts new file mode 100644 index 00000000..20dcb480 --- /dev/null +++ b/src/views/Generator/GeneratorTabs/RequestList/index.ts @@ -0,0 +1 @@ +export * from './RequestList' From 159ded2c108025975b3632be6390231fcd46355b Mon Sep 17 00:00:00 2001 From: Edgar Fisher <fisher.edgar@gmail.com> Date: Wed, 19 Mar 2025 10:45:54 +0200 Subject: [PATCH 3/3] fix: correlation form crashing when rule is disabled --- .../RuleEditor/CorrelationEditor.tsx | 51 +++++++++++-------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/src/views/Generator/RuleEditor/CorrelationEditor.tsx b/src/views/Generator/RuleEditor/CorrelationEditor.tsx index 52ba0d85..1cd4da08 100644 --- a/src/views/Generator/RuleEditor/CorrelationEditor.tsx +++ b/src/views/Generator/RuleEditor/CorrelationEditor.tsx @@ -10,7 +10,7 @@ import { Tooltip, } from '@radix-ui/themes' -import { TestRule } from '@/types/rules' +import { RuleInstance, TestRule } from '@/types/rules' import { FilterField } from './FilterField' import { SelectorField } from './SelectorField' import { Label } from '@/components/Label' @@ -19,7 +19,6 @@ import { FieldGroup } from '@/components/Form' import { ControlledRadioGroup } from '@/components/Form/ControllerRadioGroup' import { InfoCircledIcon } from '@radix-ui/react-icons' import { useApplyRules } from '@/store/hooks/useApplyRules' -import invariant from 'tiny-invariant' const EXTRACTION_MODE_OPTIONS = [ { value: 'single', label: 'First match' }, @@ -29,11 +28,6 @@ const EXTRACTION_MODE_OPTIONS = [ export function CorrelationEditor() { const { selectedRuleInstance } = useApplyRules() - invariant( - selectedRuleInstance?.type === 'correlation', - 'Selected rule instance is not a correlation rule' - ) - const { setValue, watch, @@ -41,7 +35,6 @@ export function CorrelationEditor() { formState: { errors }, } = useFormContext<TestRule>() - const { extractedValue } = selectedRuleInstance.state const replacer = watch('replacer') const isCustomReplacerSelector = !!replacer?.selector @@ -88,19 +81,7 @@ export function CorrelationEditor() { } /> </FieldGroup> - {extractedValue && ( - <Text size="2"> - <Text color="gray">Extracted value:</Text>{' '} - <pre> - <Code>{JSON.stringify(extractedValue, null, 2)}</Code> - </pre> - </Text> - )} - {!extractedValue && ( - <Text size="2" color="gray"> - The rule does not match any requests - </Text> - )} + <ExtractedValue selectedRuleInstance={selectedRuleInstance} /> </Box> <Separator orientation="vertical" size="4" decorative /> <Box> @@ -145,5 +126,33 @@ export function CorrelationEditor() { ) } +function ExtractedValue({ + selectedRuleInstance, +}: { + selectedRuleInstance?: RuleInstance +}) { + if (selectedRuleInstance?.type !== 'correlation') { + return null + } + + const extractedValue = selectedRuleInstance?.state?.extractedValue + + if (!extractedValue) { + return ( + <Text size="2" color="gray"> + The rule does not match any requests + </Text> + ) + } + return ( + <Text size="2"> + <Text color="gray">Extracted value:</Text>{' '} + <pre> + <Code>{JSON.stringify(extractedValue, null, 2)}</Code> + </pre> + </Text> + ) +} + const replacerTooltip = 'By default, the correlation rule will replace all occurrences of the extracted value in the requests. Enable this option to fine tune your selection.'