Skip to content

Commit

Permalink
feat: adds support of recording multipart/form-data requests
Browse files Browse the repository at this point in the history
  • Loading branch information
sh3pik committed Nov 1, 2021
1 parent f005679 commit d10a4fc
Show file tree
Hide file tree
Showing 6 changed files with 823 additions and 729 deletions.
18 changes: 9 additions & 9 deletions lib/server/createRecordingServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,25 +21,25 @@ const createRecordingServer = async (config) => {
const getRecordingFileName = utils.createGetRecordingFileName();

app.use(async (req, res) => {
const { method, url, body, headers } = req;
const { method, url } = req;

let response;
const { headers, data } = utils.isFormRequest(req)
? await utils.getMultipartPayload(req)
: { headers: req.headers, data: req.body };

try {
response = await axios.request({
const response = await axios
.request({
baseURL: _target,
data: body,
data,
url,
method,
headers: {
...headers,
host: null,
'Cache-Control': 'no-cache',
},
});
} catch (error) {
response = error.response; // eslint-disable-line
}
})
.catch((error) => error.response);

logger.debug(`Recorded ${method} ${url} [${response.status}]`);
res.status(response.status || 500);
Expand Down
42 changes: 42 additions & 0 deletions lib/server/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import path from 'path';
import fse from 'fs-extra';
import tar from 'tar';
import md5File from 'md5-file/promise';
import FormData from 'form-data';
import formidable from 'formidable';
// eslint-disable-next-line import/no-unresolved
import fs from 'fs/promises';

export const getRequestKey = (method, url) => {
return `${method}-${_.kebabCase(url)}`;
Expand Down Expand Up @@ -85,3 +89,41 @@ export const isDirectoryInSyncWithArchive = async (archive, directory) => {

return directoryHash === archiveHash;
};

export const isFormRequest = (request) => {
return /^multipart\/form-data/.test(request.headers['content-type']);
};

export const getMultipartPayload = async (request) => {
const { files, fields } = await parseForm(request);

const data = new FormData();
Object.entries(fields).forEach(([key, value]) => {
data.append(key, value);
});
await Promise.all(
Object.entries(files).map(async ([key, value]) => {
const buffer = await fs.readFile(value.path);
data.append(key, buffer, value.name);
}),
);

return {
headers: {
...request.headers,
...data.getHeaders(),
},
data: data.getBuffer(),
};
};

function parseForm(request) {
return new Promise((resolve, reject) => {
const form = formidable({ multiples: true });

form.parse(request, (error, fields, files) => {
if (error) reject(error);
resolve({ fields, files });
});
});
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
"body-parser": "^1.19.0",
"chalk": "^2.4.2",
"express": "^4.17.1",
"form-data": "^4.0.0",
"formidable": "^1.2.2",
"fs-extra": "^8.0.1",
"lodash": "^4.17.13",
"md5-file": "^4.0.0",
Expand Down
17 changes: 17 additions & 0 deletions tests/createRecordingServer.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import fse from 'fs-extra';
import axios from 'axios';
import FormData from 'form-data';
import createTargetServer from './helpers/createTargetServer';
import { createRecordingServer } from '../lib';

Expand Down Expand Up @@ -45,6 +46,22 @@ describe('recordingServer', () => {
expect(fse.existsSync(`${RECORDING_DIR}/POST-static-1.json`)).toEqual(true);
});

it('proxies multipart/form-data request and record them', async () => {
const data = new FormData();
data.append('name', 'Fred');

const response = await axios({
url: `${RECORDING_SERVER_URL}/multipart`,
method: 'post',
data: data.getBuffer(),
headers: {
...data.getHeaders(),
},
});
expect(response.data).toEqual({ data: 'post-static--multipart' });
expect(fse.existsSync(`${RECORDING_DIR}/POST-multipart-1.json`)).toEqual(true);
});

it('proxies errors request and record them', async () => {
await expect(axios.post(`${RECORDING_SERVER_URL}/not-found`)).rejects.toThrow('Request failed with status code 404');
expect(fse.existsSync(`${RECORDING_DIR}/POST-not-found-1.json`)).toEqual(true);
Expand Down
4 changes: 4 additions & 0 deletions tests/helpers/createTargetServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ const createTargetServer = async (config) => {
res.json({ data: `post-static--${req.body.data}` });
});

app.post('/multipart', (req, res) => {
res.json({ data: 'post-static--multipart' });
});

let server;
await new Promise((resolve) => {
server = app.listen(_port, resolve);
Expand Down
Loading

0 comments on commit d10a4fc

Please sign in to comment.