mirror of
https://github.com/Kong/insomnia
synced 2024-11-08 06:39:48 +00:00
Add filters for the ws event log (#5243)
* add filters for the ws event log
This commit is contained in:
parent
7eadb9a815
commit
54d995ec34
@ -333,7 +333,6 @@ export const GraphQLEditor: FC<Props> = ({
|
|||||||
};
|
};
|
||||||
const handleQueryUserActivity = () => {
|
const handleQueryUserActivity = () => {
|
||||||
const newOperationName = getCurrentOperation();
|
const newOperationName = getCurrentOperation();
|
||||||
console.log('newOperationName', newOperationName);
|
|
||||||
|
|
||||||
const { query, variables, operationName } = state.body;
|
const { query, variables, operationName } = state.body;
|
||||||
if (newOperationName !== operationName) {
|
if (newOperationName !== operationName) {
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import React, { FC, useEffect, useState } from 'react';
|
import { SvgIcon } from 'insomnia-components';
|
||||||
|
import React, { FC, useEffect, useRef, useState } from 'react';
|
||||||
import { useSelector } from 'react-redux';
|
import { useSelector } from 'react-redux';
|
||||||
import { Tab, TabList, TabPanel, Tabs } from 'react-tabs';
|
import { Tab, TabList, TabPanel, Tabs } from 'react-tabs';
|
||||||
import styled from 'styled-components';
|
import styled from 'styled-components';
|
||||||
@ -10,6 +11,7 @@ import { WebSocketEvent } from '../../../main/network/websocket';
|
|||||||
import { WebSocketResponse } from '../../../models/websocket-response';
|
import { WebSocketResponse } from '../../../models/websocket-response';
|
||||||
import { useWebSocketConnectionEvents } from '../../context/websocket-client/use-ws-connection-events';
|
import { useWebSocketConnectionEvents } from '../../context/websocket-client/use-ws-connection-events';
|
||||||
import { selectActiveResponse } from '../../redux/selectors';
|
import { selectActiveResponse } from '../../redux/selectors';
|
||||||
|
import { Button } from '../base/button';
|
||||||
import { ResponseHistoryDropdown } from '../dropdowns/response-history-dropdown';
|
import { ResponseHistoryDropdown } from '../dropdowns/response-history-dropdown';
|
||||||
import { ErrorBoundary } from '../error-boundary';
|
import { ErrorBoundary } from '../error-boundary';
|
||||||
import { EmptyStatePane } from '../panes/empty-state-pane';
|
import { EmptyStatePane } from '../panes/empty-state-pane';
|
||||||
@ -48,6 +50,39 @@ const PaneBodyContent = styled.div({
|
|||||||
gridTemplateRows: 'repeat(auto-fit, minmax(0, 1fr))',
|
gridTemplateRows: 'repeat(auto-fit, minmax(0, 1fr))',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const EventSearchFormControl = styled.div({
|
||||||
|
outline: 'none',
|
||||||
|
width: '100%',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
position: 'relative',
|
||||||
|
display: 'flex',
|
||||||
|
border: '1px solid var(--hl-md)',
|
||||||
|
borderRadius: 'var(--radius-md)',
|
||||||
|
});
|
||||||
|
|
||||||
|
const EventSearchInput = styled.input({
|
||||||
|
paddingRight: '2em',
|
||||||
|
padding: 'var(--padding-sm)',
|
||||||
|
backgroundColor: 'var(--hl-xxs)',
|
||||||
|
width: '100%',
|
||||||
|
display: 'block',
|
||||||
|
boxSizing: 'border-box',
|
||||||
|
|
||||||
|
// Remove the default search input cancel button
|
||||||
|
'::-webkit-search-cancel-button': {
|
||||||
|
display: 'none',
|
||||||
|
},
|
||||||
|
|
||||||
|
':focus': {
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
borderColor: 'var(--hl-lg)',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const PaddedButton = styled(Button)({
|
||||||
|
padding: 'var(--padding-sm)',
|
||||||
|
});
|
||||||
|
|
||||||
export const WebSocketResponsePane: FC<{ requestId: string }> =
|
export const WebSocketResponsePane: FC<{ requestId: string }> =
|
||||||
({
|
({
|
||||||
requestId,
|
requestId,
|
||||||
@ -80,13 +115,50 @@ const WebSocketActiveResponsePane: FC<{ requestId: string; response: WebSocketRe
|
|||||||
}) => {
|
}) => {
|
||||||
const [selectedEvent, setSelectedEvent] = useState<WebSocketEvent | null>(null);
|
const [selectedEvent, setSelectedEvent] = useState<WebSocketEvent | null>(null);
|
||||||
const [timeline, setTimeline] = useState<ResponseTimelineEntry[]>([]);
|
const [timeline, setTimeline] = useState<ResponseTimelineEntry[]>([]);
|
||||||
const events = useWebSocketConnectionEvents({ responseId: response._id });
|
|
||||||
|
const searchInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
const [clearEventsBefore, setClearEventsBefore] = useState<number | null>(null);
|
||||||
|
const [searchQuery, setSearchQuery] = useState('');
|
||||||
|
const [eventType, setEventType] = useState<WebSocketEvent['type']>();
|
||||||
|
const allEvents = useWebSocketConnectionEvents({ responseId: response._id });
|
||||||
const handleSelection = (event: WebSocketEvent) => {
|
const handleSelection = (event: WebSocketEvent) => {
|
||||||
setSelectedEvent((selected: WebSocketEvent | null) => selected?._id === event._id ? null : event);
|
setSelectedEvent((selected: WebSocketEvent | null) => selected?._id === event._id ? null : event);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const events = allEvents.filter(event => {
|
||||||
|
// Filter out events that are earlier than the clearEventsBefore timestamp
|
||||||
|
if (clearEventsBefore && event.timestamp <= clearEventsBefore) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out events that don't match the selected event type
|
||||||
|
if (eventType && event.type !== eventType) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out events that don't match the search query
|
||||||
|
if (searchQuery) {
|
||||||
|
if (event.type === 'message') {
|
||||||
|
return event.data.toString().toLowerCase().includes(searchQuery.toLowerCase());
|
||||||
|
}
|
||||||
|
if (event.type === 'error') {
|
||||||
|
return event.message.toLowerCase().includes(searchQuery.toLowerCase());
|
||||||
|
}
|
||||||
|
if (event.type === 'close') {
|
||||||
|
return event.reason.toLowerCase().includes(searchQuery.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter out open events
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setSelectedEvent(null);
|
setSelectedEvent(null);
|
||||||
|
setSearchQuery('');
|
||||||
|
setClearEventsBefore(null);
|
||||||
}, [response._id]);
|
}, [response._id]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -149,15 +221,58 @@ const WebSocketActiveResponsePane: FC<{ requestId: string; response: WebSocketRe
|
|||||||
<PaneBodyContent>
|
<PaneBodyContent>
|
||||||
{response.error ? <ResponseErrorViewer url={response.url} error={response.error} />
|
{response.error ? <ResponseErrorViewer url={response.url} error={response.error} />
|
||||||
: <>
|
: <>
|
||||||
{Boolean(events?.length) && (
|
<EventLogTableWrapper>
|
||||||
<EventLogTableWrapper>
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: 'var(--padding-sm)',
|
||||||
|
gap: 'var(--padding-sm)',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<select onChange={e => setEventType(e.currentTarget.value as WebSocketEvent['type'])}>
|
||||||
|
<option value="">All</option>
|
||||||
|
<option value="message">Message</option>
|
||||||
|
<option value="open">Open</option>
|
||||||
|
<option value="close">Close</option>
|
||||||
|
<option value="error">Error</option>
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<EventSearchFormControl>
|
||||||
|
<EventSearchInput
|
||||||
|
ref={searchInputRef}
|
||||||
|
type="search"
|
||||||
|
placeholder="Search"
|
||||||
|
value={searchQuery}
|
||||||
|
onChange={e => setSearchQuery(e.currentTarget.value)}
|
||||||
|
/>
|
||||||
|
{searchQuery && (
|
||||||
|
<PaddedButton
|
||||||
|
className="form-control__right"
|
||||||
|
onClick={() => {
|
||||||
|
setSearchQuery('');
|
||||||
|
searchInputRef.current?.focus();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<i className="fa fa-times-circle" />
|
||||||
|
</PaddedButton>
|
||||||
|
)}
|
||||||
|
</EventSearchFormControl>
|
||||||
|
<PaddedButton
|
||||||
|
onClick={() => {
|
||||||
|
const lastEvent = events[0];
|
||||||
|
setClearEventsBefore(lastEvent.timestamp);
|
||||||
|
}}
|
||||||
|
><SvgIcon icon='prohibited' /></PaddedButton>
|
||||||
|
</div>
|
||||||
|
{Boolean(events?.length) && (
|
||||||
<EventLogView
|
<EventLogView
|
||||||
events={events}
|
events={events}
|
||||||
onSelect={handleSelection}
|
onSelect={handleSelection}
|
||||||
selectionId={selectedEvent?._id}
|
selectionId={selectedEvent?._id}
|
||||||
/>
|
/>
|
||||||
</EventLogTableWrapper>
|
)}
|
||||||
)}
|
</EventLogTableWrapper>
|
||||||
{selectedEvent && (
|
{selectedEvent && (
|
||||||
<EventViewWrapper>
|
<EventViewWrapper>
|
||||||
<EventView
|
<EventView
|
||||||
|
@ -1,10 +1,15 @@
|
|||||||
import { useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useInterval } from 'react-use';
|
import { useInterval } from 'react-use';
|
||||||
|
|
||||||
import { WebSocketEvent } from '../../../main/network/websocket';
|
import { WebSocketEvent } from '../../../main/network/websocket';
|
||||||
|
|
||||||
export function useWebSocketConnectionEvents({ responseId }: { responseId: string }) {
|
export function useWebSocketConnectionEvents({ responseId }: { responseId: string }) {
|
||||||
const [events, setEvents] = useState<WebSocketEvent[]>([]);
|
const [events, setEvents] = useState<WebSocketEvent[]>([]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setEvents([]);
|
||||||
|
}, [responseId]);
|
||||||
|
|
||||||
useInterval(
|
useInterval(
|
||||||
() => {
|
() => {
|
||||||
let isMounted = true;
|
let isMounted = true;
|
||||||
@ -21,5 +26,6 @@ export function useWebSocketConnectionEvents({ responseId }: { responseId: strin
|
|||||||
},
|
},
|
||||||
500
|
500
|
||||||
);
|
);
|
||||||
|
|
||||||
return events;
|
return events;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user