Skip to content

Commit

Permalink
add websocket response model
Browse files Browse the repository at this point in the history
  • Loading branch information
gatzjames committed Sep 7, 2022
1 parent 12b7326 commit 1453d10
Show file tree
Hide file tree
Showing 12 changed files with 89 additions and 67 deletions.
28 changes: 12 additions & 16 deletions packages/insomnia/src/main/network/websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ import { webSocketRequest } from '../../models';
import * as models from '../../models';
import { Environment } from '../../models/environment';
import { RequestAuthentication, RequestHeader } from '../../models/request';
import type { Response } from '../../models/response';
import { BaseWebSocketRequest } from '../../models/websocket-request';
import type { WebSocketResponse } from '../../models/websocket-response';
import { getBasicAuthHeader } from '../../network/basic-auth/get-header';
import { getBearerAuthHeader } from '../../network/bearer-auth/get-header';
import { urlMatchesCertHost } from '../../network/url-matches-cert-host';
Expand Down Expand Up @@ -219,7 +219,7 @@ const createWebSocketConnection = async (
const internalRequestHeader = ws._req._header;
const { timeline, responseHeaders, statusCode, statusMessage, httpVersion } = parseResponseAndBuildTimeline(options.url, incomingMessage, internalRequestHeader);
timeline.map(t => timelineFileStreams.get(options.requestId)?.write(JSON.stringify(t) + '\n'));
const responsePatch: Partial<Response> = {
const responsePatch: Partial<WebSocketResponse> = {
_id: responseId,
parentId: request._id,
environmentId: responseEnvironmentId,
Expand All @@ -230,20 +230,18 @@ const createWebSocketConnection = async (
httpVersion,
elapsedTime: performance.now() - start,
timelinePath,
bodyPath: responseBodyPath,
// NOTE: required for legacy zip workaround
bodyCompression: null,
eventLogPath: responseBodyPath,
};
const settings = await models.settings.getOrCreate();
models.response.create(responsePatch, settings.maxHistoryResponses);
models.webSocketResponse.create(responsePatch, settings.maxHistoryResponses);
models.requestMeta.updateOrCreateByParentId(request._id, { activeResponseId: null });
});
ws.on('unexpected-response', async (clientRequest, incomingMessage) => {
// @ts-expect-error -- private property
const internalRequestHeader = clientRequest._header;
const { timeline, responseHeaders, statusCode, statusMessage, httpVersion } = parseResponseAndBuildTimeline(options.url, incomingMessage, internalRequestHeader);
timeline.map(t => timelineFileStreams.get(options.requestId)?.write(JSON.stringify(t) + '\n'));
const responsePatch: Partial<Response> = {
const responsePatch: Partial<WebSocketResponse> = {
_id: responseId,
parentId: request._id,
environmentId: responseEnvironmentId,
Expand All @@ -254,12 +252,10 @@ const createWebSocketConnection = async (
httpVersion,
elapsedTime: performance.now() - start,
timelinePath,
bodyPath: responseBodyPath,
// NOTE: required for legacy zip workaround
bodyCompression: null,
eventLogPath: responseBodyPath,
};
const settings = await models.settings.getOrCreate();
models.response.create(responsePatch, settings.maxHistoryResponses);
models.webSocketResponse.create(responsePatch, settings.maxHistoryResponses);
models.requestMeta.updateOrCreateByParentId(request._id, { activeResponseId: null });
deleteRequestMaps(request._id, `Unexpected response ${incomingMessage.statusCode}`);
});
Expand Down Expand Up @@ -348,7 +344,7 @@ const createErrorResponse = async (responseId: string, requestId: string, enviro
statusMessage: 'Error',
error: message,
};
models.response.create(responsePatch, settings.maxHistoryResponses);
models.webSocketResponse.create(responsePatch, settings.maxHistoryResponses);
models.requestMeta.updateOrCreateByParentId(requestId, { activeResponseId: null });
};

Expand Down Expand Up @@ -401,7 +397,7 @@ const sendWebSocketEvent = async (
};

eventLogFileStreams.get(options.requestId)?.write(JSON.stringify(lastMessage) + '\n');
const response = await models.response.getLatestByParentId(options.requestId);
const response = await models.webSocketResponse.getLatestByParentId(options.requestId);
if (!response) {
console.error('something went wrong');
return;
Expand All @@ -427,11 +423,11 @@ const closeAllWebSocketConnections = (): void => {
const findMany = async (
options: { responseId: string }
): Promise<WebSocketEvent[]> => {
const response = await models.response.getById(options.responseId);
if (!response || !response.bodyPath) {
const response = await models.webSocketResponse.getById(options.responseId);
if (!response || !response.eventLogPath) {
return [];
}
const body = await fs.promises.readFile(response.bodyPath);
const body = await fs.promises.readFile(response.eventLogPath);
return body.toString().split('\n').filter(e => e?.trim())
.map(e => JSON.parse(e)) || [];
};
Expand Down
3 changes: 3 additions & 0 deletions packages/insomnia/src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import * as _unitTest from './unit-test';
import * as _unitTestResult from './unit-test-result';
import * as _unitTestSuite from './unit-test-suite';
import * as _webSocketRequest from './websocket-request';
import * as _webSocketResponse from './websocket-response';
import * as _workspace from './workspace';
import * as _workspaceMeta from './workspace-meta';

Expand Down Expand Up @@ -78,6 +79,7 @@ export const protoDirectory = _protoDirectory;
export const grpcRequest = _grpcRequest;
export const grpcRequestMeta = _grpcRequestMeta;
export const webSocketRequest = _webSocketRequest;
export const webSocketResponse = _webSocketResponse;
export const workspace = _workspace;
export const workspaceMeta = _workspaceMeta;

Expand Down Expand Up @@ -112,6 +114,7 @@ export function all() {
grpcRequest,
grpcRequestMeta,
webSocketRequest,
webSocketResponse,
] as const;
}

Expand Down
10 changes: 5 additions & 5 deletions packages/insomnia/src/models/response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,19 +62,19 @@ export function init(): BaseResponse {
contentType: '',
url: '',
bytesRead: 0,
bytesContent: -1,
// -1 means that it was legacy and this property didn't exist yet
bytesContent: -1,
elapsedTime: 0,
headers: [],
timelinePath: '',
// Actual timelines are stored on the filesystem
bodyPath: '',
timelinePath: '',
// Actual bodies are stored on the filesystem
bodyCompression: '__NEEDS_MIGRATION__',
bodyPath: '',
// For legacy bodies
bodyCompression: '__NEEDS_MIGRATION__',
error: '',
requestVersionId: null,
// Things from the request
requestVersionId: null,
settingStoreCookies: null,
settingSendCookies: null,
// Responses sent before environment filtering will have a special value
Expand Down
2 changes: 1 addition & 1 deletion packages/insomnia/src/models/websocket-response.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export interface BaseWebSocketResponse {

export type WebSocketResponse = BaseModel & BaseWebSocketResponse;

export const isResponse = (model: Pick<BaseModel, 'type'>): model is Response => (
export const isWebSocketResponse = (model: Pick<BaseModel, 'type'>): model is WebSocketResponse => (
model.type === type
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { getPreviewModeName, PREVIEW_MODES, PreviewMode } from '../../../common/
import { exportHarCurrentRequest } from '../../../common/har';
import * as models from '../../../models';
import { isRequest } from '../../../models/request';
import { isResponse } from '../../../models/response';
import { selectActiveRequest, selectActiveResponse, selectResponsePreviewMode } from '../../redux/selectors';
import { Dropdown } from '../base/dropdown/dropdown';
import { DropdownButton } from '../base/dropdown/dropdown-button';
Expand Down Expand Up @@ -36,7 +37,7 @@ export const PreviewModeDropdown: FC<Props> = ({
const handleDownloadNormal = useCallback(() => download(false), [download]);

const exportAsHAR = useCallback(async () => {
if (!response || !request || !isRequest(request)) {
if (!response || !request || !isRequest(request) || !isResponse(response)) {
console.warn('Nothing to download');
return;
}
Expand All @@ -61,7 +62,7 @@ export const PreviewModeDropdown: FC<Props> = ({
}, [request, response]);

const exportDebugFile = useCallback(async () => {
if (!response || !request) {
if (!response || !request || !isResponse(response)) {
console.warn('Nothing to download');
return;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { differenceInHours, differenceInMinutes, isThisWeek, isToday } from 'date-fns';
import React, { FC, Fragment, useCallback, useRef } from 'react';
import React, { Fragment, useCallback, useRef } from 'react';
import { useSelector } from 'react-redux';

import { hotKeyRefs } from '../../../common/hotkeys';
import { executeHotKey } from '../../../common/hotkeys-listener';
import { decompressObject } from '../../../common/misc';
import * as models from '../../../models/index';
import type { Response } from '../../../models/response';
import { Response } from '../../../models/response';
import { isWebSocketResponse, WebSocketResponse } from '../../../models/websocket-response';
import { selectActiveEnvironment, selectActiveRequest, selectActiveRequestResponses, selectRequestVersions } from '../../redux/selectors';
import { type DropdownHandle, Dropdown } from '../base/dropdown/dropdown';
import { DropdownButton } from '../base/dropdown/dropdown-button';
Expand All @@ -20,26 +21,26 @@ import { TimeTag } from '../tags/time-tag';
import { URLTag } from '../tags/url-tag';
import { TimeFromNow } from '../time-from-now';

interface Props {
activeResponse: Response;
handleSetActiveResponse: (requestId: string, activeResponse: Response | null) => void;
interface Props<GenericResponse extends Response | WebSocketResponse> {
activeResponse: GenericResponse;
handleSetActiveResponse: (requestId: string, activeResponse: GenericResponse | null) => void;
className?: string;
requestId: string;
}

export const ResponseHistoryDropdown: FC<Props> = ({
export const ResponseHistoryDropdown = <GenericResponse extends Response | WebSocketResponse>({
activeResponse,
handleSetActiveResponse,
className,
requestId,
}) => {
}: Props<GenericResponse>) => {
const dropdownRef = useRef<DropdownHandle>(null);
const activeEnvironment = useSelector(selectActiveEnvironment);
const responses = useSelector(selectActiveRequestResponses);
const responses = useSelector(selectActiveRequestResponses) as GenericResponse[];
const activeRequest = useSelector(selectActiveRequest);
const requestVersions = useSelector(selectRequestVersions);
const now = new Date();
const categories: Record<string, Response[]> = {
const categories: Record<string, GenericResponse[]> = {
minutes: [],
hours: [],
today: [],
Expand All @@ -49,16 +50,24 @@ export const ResponseHistoryDropdown: FC<Props> = ({

const handleDeleteResponses = useCallback(async () => {
const environmentId = activeEnvironment ? activeEnvironment._id : null;
await models.response.removeForRequest(requestId, environmentId);
if (isWebSocketResponse(activeResponse)) {
await models.webSocketResponse.removeForRequest(requestId, environmentId);
} else {
await models.response.removeForRequest(requestId, environmentId);
}

if (activeRequest && activeRequest._id === requestId) {
handleSetActiveResponse(requestId, null);
}
}, [activeEnvironment, activeRequest, handleSetActiveResponse, requestId]);
}, [activeEnvironment, activeRequest, activeResponse, handleSetActiveResponse, requestId]);

const handleDeleteResponse = useCallback(async () => {
if (activeResponse) {
await models.response.remove(activeResponse);
if (isWebSocketResponse(activeResponse)) {
await models.webSocketResponse.remove(activeResponse);
} else {
await models.response.remove(activeResponse);
}
}
handleSetActiveResponse(requestId, null);
}, [activeResponse, handleSetActiveResponse, requestId]);
Expand Down Expand Up @@ -89,7 +98,7 @@ export const ResponseHistoryDropdown: FC<Props> = ({
categories.other.push(response);
});

const renderResponseRow = (response: Response) => {
const renderResponseRow = (response: GenericResponse) => {
const activeResponseId = activeResponse ? activeResponse._id : 'n/a';
const active = response._id === activeResponseId;
const requestVersion = requestVersions.find(({ _id }) => _id === response.requestVersionId);
Expand All @@ -114,12 +123,14 @@ export const ResponseHistoryDropdown: FC<Props> = ({
tooltipDelay={1000}
/>
<TimeTag milliseconds={response.elapsedTime} small tooltipDelay={1000} />
<SizeTag
bytesRead={response.bytesRead}
bytesContent={response.bytesContent}
small
tooltipDelay={1000}
/>
{!isWebSocketResponse(response) && (
<SizeTag
bytesRead={response.bytesRead}
bytesContent={response.bytesContent}
small
tooltipDelay={1000}
/>
)}
{!response.requestVersionId ?
<i
className="icon fa fa-info-circle"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export const ResponsePane: FC<Props> = ({
handleSetActiveResponse,
request,
}) => {
const response = useSelector(selectActiveResponse);
const response = useSelector(selectActiveResponse) as Response | null;
const filterHistory = useSelector(selectResponseFilterHistory);
const filter = useSelector(selectResponseFilter);
const settings = useSelector(selectSettings);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import fs from 'fs';
import React, { FC, useEffect, useState } from 'react';
import { useSelector } from 'react-redux';
import { Tab, TabList, TabPanel, Tabs } from 'react-tabs';
import styled from 'styled-components';

import { getSetCookieHeaders } from '../../../common/misc';
import { ResponseTimelineEntry } from '../../../main/network/libcurl-promise';
import { WebSocketEvent } from '../../../main/network/websocket';
import type { Response } from '../../../models/response';
import { WebSocketResponse } from '../../../models/websocket-response';
import { useWebSocketConnectionEvents } from '../../context/websocket-client/use-ws-connection-events';
import { selectActiveResponse } from '../../redux/selectors';
import { ResponseHistoryDropdown } from '../dropdowns/response-history-dropdown';
import { ErrorBoundary } from '../error-boundary';
import { Pane, PaneHeader as OriginalPaneHeader } from '../panes/pane';
Expand Down Expand Up @@ -47,12 +49,12 @@ const PaneBodyContent = styled.div({
gridTemplateRows: 'repeat(auto-fit, minmax(0, 1fr))',
});

export const WebSocketResponsePane: FC<{ requestId: string; response: Response | null; handleSetActiveResponse: (requestId: string, activeResponse: Response | null) => void }> =
export const WebSocketResponsePane: FC<{ requestId: string; handleSetActiveResponse: (requestId: string, activeResponse: WebSocketResponse | null) => void }> =
({
requestId,
response,
handleSetActiveResponse,
}) => {
const response = useSelector(selectActiveResponse) as WebSocketResponse | null;
if (!response) {
return (
<Pane type="response">
Expand All @@ -63,7 +65,7 @@ export const WebSocketResponsePane: FC<{ requestId: string; response: Response |
return <WebSocketActiveResponsePane requestId={requestId} response={response} handleSetActiveResponse={handleSetActiveResponse} />;
};

const WebSocketActiveResponsePane: FC<{ requestId: string; response: Response; handleSetActiveResponse: (requestId: string, activeResponse: Response | null) => void }> = ({
const WebSocketActiveResponsePane: FC<{ requestId: string; response: WebSocketResponse; handleSetActiveResponse: (requestId: string, activeResponse: WebSocketResponse | null) => void }> = ({
requestId,
response,
handleSetActiveResponse,
Expand All @@ -75,7 +77,7 @@ const WebSocketActiveResponsePane: FC<{ requestId: string; response: Response; h
setSelectedEvent((selected: WebSocketEvent | null) => selected?._id === event._id ? null : event);
};

const setActiveResponseAndDisconnect = (requestId: string, response: Response | null) => {
const setActiveResponseAndDisconnect = (requestId: string, response: WebSocketResponse | null) => {
handleSetActiveResponse(requestId, response);
window.main.webSocket.close({ requestId });
};
Expand Down
7 changes: 2 additions & 5 deletions packages/insomnia/src/ui/components/wrapper-debug.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import { isRemoteProject } from '../../models/project';
import { Request, RequestHeader } from '../../models/request';
import type { Response } from '../../models/response';
import { isWebSocketRequest } from '../../models/websocket-request';
import { WebSocketResponse } from '../../models/websocket-response';
import { isCollection, isDesign } from '../../models/workspace';
import { VCS } from '../../sync/vcs/vcs';
import {
selectActiveEnvironment,
selectActiveProject,
selectActiveRequest,
selectActiveResponse,
selectActiveWorkspace,
selectIsLoggedIn,
selectSettings,
Expand All @@ -39,7 +39,7 @@ interface Props {
gitSyncDropdown: ReactNode;
handleActivityChange: HandleActivityChange;
handleSetActiveEnvironment: (id: string | null) => void;
handleSetActiveResponse: (requestId: string, activeResponse: Response | null) => void;
handleSetActiveResponse: (requestId: string, activeResponse: Response | WebSocketResponse | null) => void;
handleForceUpdateRequest: (r: Request, patch: Partial<Request>) => Promise<Request>;
handleForceUpdateRequestHeaders: (r: Request, headers: RequestHeader[]) => Promise<Request>;
handleImport: Function;
Expand All @@ -64,13 +64,11 @@ export const WrapperDebug: FC<Props> = ({
headerEditorKey,
vcs,
}) => {

const activeProject = useSelector(selectActiveProject);
const isLoggedIn = useSelector(selectIsLoggedIn);

const activeEnvironment = useSelector(selectActiveEnvironment);
const activeRequest = useSelector(selectActiveRequest);
const activeResponse = useSelector(selectActiveResponse);
const activeWorkspace = useSelector(selectActiveWorkspace);
const settings = useSelector(selectSettings);
const sidebarFilter = useSelector(selectSidebarFilter);
Expand Down Expand Up @@ -171,7 +169,6 @@ export const WrapperDebug: FC<Props> = ({
isWebSocketRequest(activeRequest) ? (
<WebSocketResponsePane
requestId={activeRequest._id}
response={activeResponse}
handleSetActiveResponse={handleSetActiveResponse}
/>
) : (
Expand Down
Loading

0 comments on commit 1453d10

Please sign in to comment.