mirror of
https://github.com/Kong/insomnia.git
synced 2026-08-04 11:52:33 -04:00
fix: large response save to file
This commit is contained in:
committed by
Jay Wu
parent
4f5a72dc16
commit
6fefc7ff60
@@ -3,12 +3,13 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import { downloadResponseBody } from '../response-pane-utils';
|
||||
|
||||
const mockWriteFile = vi.fn();
|
||||
const mockWriteResponseBodyToFile = vi.fn();
|
||||
const mockShowSaveDialog = vi.fn();
|
||||
|
||||
beforeEach(() => {
|
||||
vi.stubGlobal('window', {
|
||||
dialog: { showSaveDialog: mockShowSaveDialog },
|
||||
main: { writeFile: mockWriteFile },
|
||||
main: { writeFile: mockWriteFile, writeResponseBodyToFile: mockWriteResponseBodyToFile },
|
||||
});
|
||||
});
|
||||
|
||||
@@ -78,6 +79,23 @@ describe('downloadResponseBody', () => {
|
||||
expect(content).toContain('"b": 2');
|
||||
expect(content).toContain('"a": 1');
|
||||
});
|
||||
|
||||
it('reads the response body before prettifying when it is not stored in memory', async () => {
|
||||
mockShowSaveDialog.mockResolvedValue({ canceled: false, filePath: '/tmp/out.json' });
|
||||
const getBodyBuffer = vi.fn().mockResolvedValue(new TextEncoder().encode('{"b":2,"a":1}'));
|
||||
|
||||
await downloadResponseBody(
|
||||
{ name: 'My Request' },
|
||||
{ contentType: 'application/json', bodyBuffer: null },
|
||||
true,
|
||||
getBodyBuffer,
|
||||
);
|
||||
|
||||
expect(getBodyBuffer).toHaveBeenCalledOnce();
|
||||
expect(mockWriteFile).toHaveBeenCalledOnce();
|
||||
expect(mockWriteFile.mock.calls[0][0].content).toContain('"b": 2');
|
||||
expect(mockWriteFile.mock.calls[0][0].content).toContain('"a": 1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('raw-bytes branch (default)', () => {
|
||||
@@ -93,6 +111,7 @@ describe('downloadResponseBody', () => {
|
||||
expect(path).toBe('/tmp/out.png');
|
||||
expect(content).toBeInstanceOf(Uint8Array);
|
||||
expect(content).toEqual(binaryData);
|
||||
expect(mockWriteResponseBodyToFile).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('writes raw bytes when prettify is true but the content-type is not JSON', async () => {
|
||||
@@ -105,6 +124,7 @@ describe('downloadResponseBody', () => {
|
||||
const { content } = mockWriteFile.mock.calls[0][0];
|
||||
expect(content).toBeInstanceOf(Uint8Array);
|
||||
expect(content).toEqual(textData);
|
||||
expect(mockWriteResponseBodyToFile).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('writes empty bytes when bodyBuffer is null', async () => {
|
||||
@@ -120,6 +140,48 @@ describe('downloadResponseBody', () => {
|
||||
const { content } = mockWriteFile.mock.calls[0][0];
|
||||
expect(content).toBeInstanceOf(Uint8Array);
|
||||
expect(content.length).toBe(0);
|
||||
expect(mockWriteResponseBodyToFile).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('copies the response body file when bodyBuffer is null and bodyPath is available', async () => {
|
||||
mockShowSaveDialog.mockResolvedValue({ canceled: false, filePath: '/tmp/out.bin' });
|
||||
|
||||
await downloadResponseBody(
|
||||
{ name: 'My Request' },
|
||||
{
|
||||
contentType: 'application/octet-stream',
|
||||
bodyBuffer: null,
|
||||
bodyPath: '/tmp/responses/abc.response',
|
||||
bodyCompression: 'zip',
|
||||
},
|
||||
false,
|
||||
);
|
||||
|
||||
expect(mockWriteResponseBodyToFile).toHaveBeenCalledOnce();
|
||||
expect(mockWriteResponseBodyToFile).toHaveBeenCalledWith({
|
||||
sourcePath: '/tmp/responses/abc.response',
|
||||
destinationPath: '/tmp/out.bin',
|
||||
bodyCompression: 'zip',
|
||||
});
|
||||
expect(mockWriteFile).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('reads the response body when it is not stored in memory', async () => {
|
||||
mockShowSaveDialog.mockResolvedValue({ canceled: false, filePath: '/tmp/out.bin' });
|
||||
const fileBody = new TextEncoder().encode('body from disk');
|
||||
const getBodyBuffer = vi.fn().mockResolvedValue(fileBody);
|
||||
|
||||
await downloadResponseBody(
|
||||
{ name: 'My Request' },
|
||||
{ contentType: 'application/octet-stream', bodyBuffer: null },
|
||||
false,
|
||||
getBodyBuffer,
|
||||
);
|
||||
|
||||
expect(getBodyBuffer).toHaveBeenCalledOnce();
|
||||
expect(mockWriteFile).toHaveBeenCalledOnce();
|
||||
expect(mockWriteFile.mock.calls[0][0].content).toEqual(fileBody);
|
||||
expect(mockWriteResponseBodyToFile).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,8 +5,17 @@ import { jsonPrettify } from '~/ui/utils/prettify/json';
|
||||
|
||||
export async function downloadResponseBody(
|
||||
activeRequest: { name: string } | null | undefined,
|
||||
activeResponse: { contentType: string; bodyBuffer?: Uint8Array | null } | null | undefined,
|
||||
activeResponse:
|
||||
| {
|
||||
contentType: string;
|
||||
bodyBuffer?: Uint8Array | null;
|
||||
bodyPath?: string;
|
||||
bodyCompression?: 'zip' | null | '__NEEDS_MIGRATION__';
|
||||
}
|
||||
| null
|
||||
| undefined,
|
||||
prettify: boolean,
|
||||
getBodyBuffer?: () => Promise<Uint8Array | null>,
|
||||
) {
|
||||
if (!activeResponse || !activeRequest) {
|
||||
console.warn('Nothing to download');
|
||||
@@ -24,12 +33,30 @@ export async function downloadResponseBody(
|
||||
if (canceled) {
|
||||
return;
|
||||
}
|
||||
if (prettify && contentType.includes('json')) {
|
||||
await window.main.writeFile({
|
||||
path: outputPath,
|
||||
content: jsonPrettify(bodyBufferToUtf8(activeResponse.bodyBuffer)) || '',
|
||||
let bodyBuffer = activeResponse.bodyBuffer ?? null;
|
||||
|
||||
if (!bodyBuffer && !prettify && activeResponse.bodyPath) {
|
||||
await window.main.writeResponseBodyToFile({
|
||||
sourcePath: activeResponse.bodyPath,
|
||||
destinationPath: outputPath,
|
||||
bodyCompression: activeResponse.bodyCompression === 'zip' ? 'zip' : null,
|
||||
});
|
||||
return;
|
||||
}
|
||||
await window.main.writeFile({ path: outputPath, content: activeResponse.bodyBuffer ?? new Uint8Array(0) });
|
||||
|
||||
if (!bodyBuffer && getBodyBuffer) {
|
||||
const diskBodyBuffer = await getBodyBuffer();
|
||||
if (diskBodyBuffer) {
|
||||
bodyBuffer = diskBodyBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
if (prettify && contentType.includes('json')) {
|
||||
await window.main.writeFile({
|
||||
path: outputPath,
|
||||
content: jsonPrettify(bodyBufferToUtf8(bodyBuffer)) || '',
|
||||
});
|
||||
return;
|
||||
}
|
||||
await window.main.writeFile({ path: outputPath, content: bodyBuffer ?? new Uint8Array(0) });
|
||||
}
|
||||
|
||||
@@ -68,7 +68,18 @@ export const ResponsePane: FC<Props> = ({ activeRequestId }) => {
|
||||
const { isExecuting, steps } = useExecutionState({ requestId: activeRequest._id });
|
||||
|
||||
const handleDownloadResponseBody = useCallback(
|
||||
(prettify: boolean) => downloadResponseBody(activeRequest, activeResponse, prettify),
|
||||
(prettify: boolean) =>
|
||||
downloadResponseBody(
|
||||
activeRequest,
|
||||
activeResponse,
|
||||
prettify,
|
||||
activeResponse
|
||||
? async () => {
|
||||
const bodyBuffer = await services.helpers.getResponseBodyBuffer(activeResponse);
|
||||
return typeof bodyBuffer === 'string' ? null : bodyBuffer;
|
||||
}
|
||||
: undefined,
|
||||
),
|
||||
[activeRequest, activeResponse],
|
||||
);
|
||||
const [timeline, setTimeline] = useState<ResponseTimelineEntry[]>([]);
|
||||
|
||||
Reference in New Issue
Block a user