Skip to content

Commit

Permalink
can select and save payload message and preview mode (#5143)
Browse files Browse the repository at this point in the history
  • Loading branch information
jackkav committed Sep 9, 2022
1 parent 2ba8bf5 commit 44a2896
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 16 deletions.
17 changes: 15 additions & 2 deletions packages/insomnia-smoke-test/server/websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,21 +45,34 @@ Location: ws://localhost:4010
`);
return;
};
const return401withBody = (socket: Socket) => {
socket.end(`HTTP/1.1 401 Unauthorized
<!doctype html>
<html>
<body>
<div>
<h1>401 Unauthorized</h1>
</div>
</body>
</html>`);
return;
};
const upgrade = (wss: WebSocketServer, request: IncomingMessage, socket: Socket, head: Buffer) => {
if (request.url === '/redirect') {
return redirectOnSuccess(socket);
}
if (request.url === '/bearer') {
if (request.headers.authorization !== 'Bearer insomnia-cool-token-!!!1112113243111') {
socket.end('HTTP/1.1 401 Unauthorized\n\n');
return401withBody(socket);
return;
}
return redirectOnSuccess(socket);
}
if (request.url === '/basic-auth') {
// login with user:password
if (request.headers.authorization !== 'Basic dXNlcjpwYXNzd29yZA==') {
socket.end('HTTP/1.1 401 Unauthorized\n\n');
return401withBody(socket);
return;
}
return redirectOnSuccess(socket);
Expand Down
1 change: 1 addition & 0 deletions packages/insomnia/src/common/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -562,6 +562,7 @@ export const BASE_ENVIRONMENT_ID_KEY = '__BASE_ENVIRONMENT_ID__';
export const EXPORT_TYPE_REQUEST = 'request';
export const EXPORT_TYPE_GRPC_REQUEST = 'grpc_request';
export const EXPORT_TYPE_WEBSOCKET_REQUEST = 'websocket_request';
export const EXPORT_TYPE_WEBSOCKET_PAYLOAD = 'websocket_payload';
export const EXPORT_TYPE_REQUEST_GROUP = 'request_group';
export const EXPORT_TYPE_UNIT_TEST_SUITE = 'unit_test_suite';
export const EXPORT_TYPE_UNIT_TEST = 'unit_test';
Expand Down
9 changes: 8 additions & 1 deletion packages/insomnia/src/common/export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { isRequest } from '../models/request';
import { isRequestGroup } from '../models/request-group';
import { isUnitTest } from '../models/unit-test';
import { isUnitTestSuite } from '../models/unit-test-suite';
import { isWebSocketPayload } from '../models/websocket-payload';
import { isWebSocketRequest } from '../models/websocket-request';
import { isWorkspace, Workspace } from '../models/workspace';
import { resetKeys } from '../sync/ignore-keys';
Expand All @@ -29,6 +30,7 @@ import {
EXPORT_TYPE_REQUEST_GROUP,
EXPORT_TYPE_UNIT_TEST,
EXPORT_TYPE_UNIT_TEST_SUITE,
EXPORT_TYPE_WEBSOCKET_PAYLOAD,
EXPORT_TYPE_WEBSOCKET_REQUEST,
EXPORT_TYPE_WORKSPACE,
getAppVersion,
Expand Down Expand Up @@ -176,7 +178,8 @@ export async function exportRequestsData(
isUnitTestSuite(d) ||
isUnitTest(d) ||
isProtoFile(d) ||
isProtoDirectory(d)
isProtoDirectory(d) ||
isWebSocketPayload(d)
);
});
docs.push(...descendants);
Expand All @@ -190,6 +193,7 @@ export async function exportRequestsData(
isUnitTestSuite(d) ||
isUnitTest(d) ||
isRequest(d) ||
isWebSocketPayload(d) ||
isWebSocketRequest(d) ||
isGrpcRequest(d) ||
isRequestGroup(d) ||
Expand Down Expand Up @@ -234,6 +238,9 @@ export async function exportRequestsData(
} else if (isGrpcRequest(d)) {
// @ts-expect-error -- TSCONVERSION maybe this needs to be added to the upstream type?
d._type = EXPORT_TYPE_GRPC_REQUEST;
} else if (isWebSocketPayload(d)) {
// @ts-expect-error -- TSCONVERSION maybe this needs to be added to the upstream type?
d._type = EXPORT_TYPE_WEBSOCKET_PAYLOAD;
} else if (isWebSocketRequest(d)) {
// @ts-expect-error -- TSCONVERSION maybe this needs to be added to the upstream type?
d._type = EXPORT_TYPE_WEBSOCKET_REQUEST;
Expand Down
3 changes: 3 additions & 0 deletions packages/insomnia/src/main/network/websocket.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ const createWebSocketConnection = async (
models.requestMeta.updateOrCreateByParentId(request._id, { activeResponseId: null });
});
ws.on('unexpected-response', async (clientRequest, incomingMessage) => {
incomingMessage.on('data', chunk => {
timelineFileStreams.get(options.requestId)?.write(JSON.stringify({ value: chunk.toString(), name: 'DataOut', timestamp: Date.now() }) + '\n');
});
// @ts-expect-error -- private property
const internalRequestHeader = clientRequest._header;
const { timeline, responseHeaders, statusCode, statusMessage, httpVersion } = parseResponseAndBuildTimeline(options.url, incomingMessage, internalRequestHeader);
Expand Down
5 changes: 5 additions & 0 deletions packages/insomnia/src/models/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
EXPORT_TYPE_REQUEST_GROUP,
EXPORT_TYPE_UNIT_TEST,
EXPORT_TYPE_UNIT_TEST_SUITE,
EXPORT_TYPE_WEBSOCKET_PAYLOAD,
EXPORT_TYPE_WEBSOCKET_REQUEST,
EXPORT_TYPE_WORKSPACE,
} from '../common/constants';
Expand Down Expand Up @@ -36,6 +37,7 @@ import * as _stats from './stats';
import * as _unitTest from './unit-test';
import * as _unitTestResult from './unit-test-result';
import * as _unitTestSuite from './unit-test-suite';
import * as _webSocketPayload from './websocket-payload';
import * as _webSocketRequest from './websocket-request';
import * as _webSocketResponse from './websocket-response';
import * as _workspace from './workspace';
Expand Down Expand Up @@ -78,6 +80,7 @@ export const protoFile = _protoFile;
export const protoDirectory = _protoDirectory;
export const grpcRequest = _grpcRequest;
export const grpcRequestMeta = _grpcRequestMeta;
export const webSocketPayload = _webSocketPayload;
export const webSocketRequest = _webSocketRequest;
export const webSocketResponse = _webSocketResponse;
export const workspace = _workspace;
Expand Down Expand Up @@ -113,6 +116,7 @@ export function all() {
protoDirectory,
grpcRequest,
grpcRequestMeta,
webSocketPayload,
webSocketRequest,
webSocketResponse,
] as const;
Expand Down Expand Up @@ -214,6 +218,7 @@ export async function initModel<T extends BaseModel>(type: string, ...sources: R

export const MODELS_BY_EXPORT_TYPE: Record<string, any> = {
[EXPORT_TYPE_REQUEST]: request,
[EXPORT_TYPE_WEBSOCKET_PAYLOAD]: webSocketPayload,
[EXPORT_TYPE_WEBSOCKET_REQUEST]: webSocketRequest,
[EXPORT_TYPE_GRPC_REQUEST]: grpcRequest,
[EXPORT_TYPE_REQUEST_GROUP]: requestGroup,
Expand Down
70 changes: 70 additions & 0 deletions packages/insomnia/src/models/websocket-payload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import { database } from '../common/database';
import type { BaseModel } from '.';

export const name = 'WebSocket Payload';

export const type = 'WebSocketPayload';

export const prefix = 'ws-payload';

export const canDuplicate = true;

// @TODO: enable this at some point
export const canSync = false;

export interface BaseWebSocketPayload {
value: string;
mode: string;
}

export type WebSocketPayload = BaseModel & BaseWebSocketPayload & { type: typeof type };

export const isWebSocketPayload = (model: Pick<BaseModel, 'type'>): model is WebSocketPayload => (
model.type === type
);

export const isWebSocketPayloadId = (id: string | null) => (
id?.startsWith(`${prefix}_`)
);

export const init = (): BaseWebSocketPayload => ({
value: '',
mode: 'application/json',
});

export const migrate = (doc: WebSocketPayload) => doc;

export const create = (patch: Partial<WebSocketPayload> = {}) => {
if (!patch.parentId) {
throw new Error(`New WebSocketPayload missing \`parentId\`: ${JSON.stringify(patch)}`);
}

return database.docCreate<WebSocketPayload>(type, patch);
};

export const remove = (obj: WebSocketPayload) => database.remove(obj);

export const update = (
obj: WebSocketPayload,
patch: Partial<WebSocketPayload> = {}
) => database.docUpdate(obj, patch);

export async function duplicate(request: WebSocketPayload, patch: Partial<WebSocketPayload> = {}) {
// Only set name and "(Copy)" if the patch does
// not define it and the request itself has a name.
// Otherwise leave it blank so the request URL can
// fill it in automatically.
if (!patch.name && request.name) {
patch.name = `${request.name} (Copy)`;
}

return database.duplicate<WebSocketPayload>(request, {
name,
...patch,
});
}

export const getById = (_id: string) => database.getWhere<WebSocketPayload>(type, { _id });
export const getByParentId = (parentId: string) => database.getWhere<WebSocketPayload>(type, { parentId });

export const all = () => database.all<WebSocketPayload>(type);
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ import { Dropdown } from '../base/dropdown/dropdown';
import { DropdownButton } from '../base/dropdown/dropdown-button';
import { DropdownItem } from '../base/dropdown/dropdown-item';
interface Props {
payloadType: string;
onClick: (payloadType: string) => void;
previewMode: string;
onClick: (previewMode: string) => void;
}
export const PayloadTypeDropdown: FC<Props> = ({ payloadType, onClick }) => {
export const WebSocketPreviewModeDropdown: FC<Props> = ({ previewMode, onClick }) => {
return (
<Dropdown>
<DropdownButton className="tall">
{{
[CONTENT_TYPE_JSON]: 'JSON',
[CONTENT_TYPE_PLAINTEXT]: 'Raw',
}[payloadType]}
}[previewMode]}
<i className="fa fa-caret-down space-left" />
</DropdownButton>
<DropdownItem onClick={onClick} value={CONTENT_TYPE_JSON}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { FC, FormEvent, useRef, useState } from 'react';
import React, { FC, FormEvent, useEffect, useRef, useState } from 'react';
import { Tab, TabList, TabPanel, Tabs } from 'react-tabs';
import styled from 'styled-components';

Expand All @@ -9,7 +9,7 @@ import { WebSocketRequest } from '../../../models/websocket-request';
import { ReadyState, useWSReadyState } from '../../context/websocket-client/use-ws-ready-state';
import { CodeEditor, UnconnectedCodeEditor } from '../codemirror/code-editor';
import { AuthDropdown } from '../dropdowns/auth-dropdown';
import { PayloadTypeDropdown } from '../dropdowns/payload-type-dropdown';
import { WebSocketPreviewModeDropdown } from '../dropdowns/websocket-preview-mode';
import { AuthWrapper } from '../editors/auth/auth-wrapper';
import { RequestHeadersEditor } from '../editors/request-headers-editor';
import { showAlert, showModal } from '../modals';
Expand Down Expand Up @@ -52,16 +52,30 @@ const PaneHeader = styled(OriginalPaneHeader)({

interface FormProps {
request: WebSocketRequest;
payloadType: string;
previewMode: string;
initialValue: string;
environmentId: string;
createOrUpdatePayload: (payload: string, mode: string) => Promise<void>;
}

const WebSocketRequestForm: FC<FormProps> = ({
request,
payloadType,
previewMode,
initialValue,
createOrUpdatePayload,
environmentId,
}) => {
const editorRef = useRef<UnconnectedCodeEditor>(null);
useEffect(() => {
let isMounted = true;
if (isMounted) {
editorRef.current?.codeMirror?.setValue(initialValue);
}
return () => {
isMounted = false;
};
}, [initialValue]);

const handleSubmit = async (event: FormEvent<HTMLFormElement>) => {
event.preventDefault();
const message = editorRef.current?.getValue() || '';
Expand Down Expand Up @@ -102,9 +116,10 @@ const WebSocketRequestForm: FC<FormProps> = ({
<EditorWrapper>
<CodeEditor
uniquenessKey={request._id}
mode={payloadType}
mode={previewMode}
ref={editorRef}
defaultValue=''
onChange={value => createOrUpdatePayload(value, previewMode)}
enableNunjucks
/>
</EditorWrapper>
Expand All @@ -125,13 +140,49 @@ interface Props {
// TODO: @gatzjames discuss above assertion in light of request and settings drills
export const WebSocketRequestPane: FC<Props> = ({ request, workspaceId, environmentId, forceRefreshKey }) => {
const readyState = useWSReadyState(request._id);

const disabled = readyState === ReadyState.OPEN || readyState === ReadyState.CLOSING;
const handleOnChange = (url: string) => {
if (url !== request.url) {
models.webSocketRequest.update(request, { url });
}
};
const [payloadType, setPayloadType] = useState(CONTENT_TYPE_JSON);
const [previewMode, setPreviewMode] = useState(CONTENT_TYPE_JSON);
const [initialValue, setInitialValue] = useState('');

useEffect(() => {
let isMounted = true;
const fn = async () => {
const payload = await models.webSocketPayload.getByParentId(request._id);
if (isMounted && payload) {
setInitialValue(payload?.value || '');
setPreviewMode(payload.mode);
}
};
fn();
return () => {
isMounted = false;
};
}, [request._id]);

const changeMode = (mode: string) => {
setPreviewMode(mode);
createOrUpdatePayload(initialValue, mode);
};

const createOrUpdatePayload = async (value: string, mode: string) => {
// @TODO: multiple payloads
const payload = await models.webSocketPayload.getByParentId(request._id);
if (payload) {
await models.webSocketPayload.update(payload, { value, mode });
return;
}
await models.webSocketPayload.create({
parentId: request._id,
value,
mode,
});
};

const uniqueKey = `${forceRefreshKey}::${request._id}`;

Expand All @@ -151,7 +202,7 @@ export const WebSocketRequestPane: FC<Props> = ({ request, workspaceId, environm
<Tabs className="pane__body theme--pane__body react-tabs">
<TabList>
<Tab tabIndex="-1" >
<PayloadTypeDropdown payloadType={payloadType} onClick={setPayloadType} />
<WebSocketPreviewModeDropdown previewMode={previewMode} onClick={changeMode} />
</Tab>
<Tab tabIndex="-1">
<AuthDropdown
Expand All @@ -176,7 +227,9 @@ export const WebSocketRequestPane: FC<Props> = ({ request, workspaceId, environm
<WebSocketRequestForm
key={uniqueKey}
request={request}
payloadType={payloadType}
previewMode={previewMode}
initialValue={initialValue}
createOrUpdatePayload={createOrUpdatePayload}
environmentId={environmentId}
/>
</TabPanel>
Expand Down
4 changes: 4 additions & 0 deletions packages/insomnia/src/ui/redux/modules/entities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import { Stats } from '../../../models/stats';
import { UnitTest } from '../../../models/unit-test';
import { UnitTestResult } from '../../../models/unit-test-result';
import { UnitTestSuite } from '../../../models/unit-test-suite';
import { WebSocketPayload } from '../../../models/websocket-payload';
import { WebSocketRequest } from '../../../models/websocket-request';
import { WebSocketResponse } from '../../../models/websocket-response';
import { Workspace } from '../../../models/workspace';
Expand Down Expand Up @@ -72,6 +73,7 @@ export interface EntitiesState {
protoDirectories: EntityRecord<ProtoDirectory>;
grpcRequests: EntityRecord<GrpcRequest>;
grpcRequestMetas: EntityRecord<GrpcRequestMeta>;
webSocketPayloads: EntityRecord<WebSocketPayload>;
webSocketRequests: EntityRecord<WebSocketRequest>;
webSocketResponses: EntityRecord<WebSocketResponse>;
}
Expand Down Expand Up @@ -102,6 +104,7 @@ export const initialEntitiesState: EntitiesState = {
protoDirectories: {},
grpcRequests: {},
grpcRequestMetas: {},
webSocketPayloads: {},
webSocketRequests: {},
webSocketResponses: {},
};
Expand Down Expand Up @@ -203,6 +206,7 @@ export async function allDocs() {
...(await models.protoDirectory.all()),
...(await models.grpcRequest.all()),
...(await models.grpcRequestMeta.all()),
...(await models.webSocketPayload.all()),
...(await models.webSocketRequest.all()),
...(await models.webSocketResponse.all()),
];
Expand Down
Loading

0 comments on commit 44a2896

Please sign in to comment.