Skip to content

Commit 3da4ae8

Browse files
authored
feat(deps): bump formdata-node from 3.7.0 to 4.0.0 (#2196)
- fix all related tests - add backward compatibility with Buffer instances Closes #2196
1 parent 4d783eb commit 3da4ae8

File tree

6 files changed

+62
-20
lines changed

6 files changed

+62
-20
lines changed

package-lock.json

Lines changed: 3 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@
115115
"deep-extend": "~0.6.0",
116116
"fast-json-patch": "^3.0.0-1",
117117
"form-data-encoder": "^1.4.3",
118-
"formdata-node": "^3.6.2",
118+
"formdata-node": "^4.0.0",
119119
"js-yaml": "^4.1.0",
120120
"lodash": "^4.17.19",
121121
"qs": "^6.9.4",

src/http/fold-formdata-to-request.node.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Readable } from 'stream';
2-
import { Encoder } from 'form-data-encoder';
2+
import { FormDataEncoder } from 'form-data-encoder';
33

44
/**
55
* formdata-node works in [email protected] via form-data-encoder only.
@@ -9,7 +9,7 @@ import { Encoder } from 'form-data-encoder';
99
* TODO([email protected]): this can be removed when migrated to [email protected]
1010
*/
1111
const foldFormDataToRequest = (formdata, request) => {
12-
const encoder = new Encoder(formdata);
12+
const encoder = new FormDataEncoder(formdata);
1313
const readableStream = Readable.from(encoder);
1414

1515
// get rid of previous headers

src/http/index.js

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import jsYaml from 'js-yaml';
44
import pick from 'lodash/pick';
55
import isFunction from 'lodash/isFunction';
66
import { Buffer } from 'buffer';
7-
import { FormData } from 'formdata-node';
7+
import { FormData, File, Blob } from 'formdata-node';
88

99
import { encodeDisallowedCharacters } from '../execute/oas3/style-serializer';
1010
import foldFormDataToRequest from './fold-formdata-to-request.node';
@@ -164,14 +164,12 @@ export function isFile(obj, navigatorObj) {
164164
}
165165

166166
if (typeof File !== 'undefined' && obj instanceof File) {
167-
// eslint-disable-line no-undef
168167
return true;
169168
}
170169
if (typeof Blob !== 'undefined' && obj instanceof Blob) {
171-
// eslint-disable-line no-undef
172170
return true;
173171
}
174-
if (typeof Buffer !== 'undefined' && obj instanceof Buffer) {
172+
if (Buffer.isBuffer(obj)) {
175173
return true;
176174
}
177175

@@ -340,6 +338,11 @@ function buildFormData(reqForm) {
340338
* Build a new FormData instance, support array as field value
341339
* OAS2.0 - when collectionFormat is multi
342340
* OAS3.0 - when explode of Encoding Object is true
341+
*
342+
* This function explicitly handles Buffers (for backward compatibility)
343+
* if provided as a values to FormData. FormData can only handle USVString
344+
* or Blob.
345+
*
343346
* @param {Object} reqForm - ori req.form
344347
* @return {FormData} - new FormData instance
345348
*/
@@ -349,8 +352,16 @@ function buildFormData(reqForm) {
349352
if (Array.isArray(value)) {
350353
// eslint-disable-next-line no-restricted-syntax
351354
for (const v of value) {
352-
formData.append(key, v);
355+
if (Buffer.isBuffer(v)) {
356+
const blob = new Blob([v]);
357+
formData.append(key, blob);
358+
} else {
359+
formData.append(key, v);
360+
}
353361
}
362+
} else if (Buffer.isBuffer(value)) {
363+
const blob = new Blob([value]);
364+
formData.append(key, blob);
354365
} else {
355366
formData.append(key, value);
356367
}

test/http/http-multipart.js

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import fetchMock from 'fetch-mock';
22
import { Readable } from 'stream';
3+
import { File } from 'formdata-node';
34

45
import { buildRequest } from '../../src/execute';
56
import sampleMultipartOpenApi2 from '../data/sample-multipart-oas2';
@@ -226,7 +227,7 @@ describe('buildRequest - openapi 3.0', () => {
226227
});
227228
});
228229

229-
describe('formData with file', () => {
230+
describe('formData with Buffer', () => {
230231
const file1 = Buffer.from('test file data1');
231232
const file2 = Buffer.from('test file data2');
232233

@@ -257,6 +258,41 @@ describe('buildRequest - openapi 3.0', () => {
257258
});
258259
});
259260

261+
describe('formData with File/Blob', () => {
262+
const file1 = new File(['test file data1'], 'file1.txt', {
263+
type: 'text/plain',
264+
});
265+
const file2 = new File(['test file data2'], 'file2.txt', {
266+
type: 'text/plain',
267+
});
268+
269+
const req = buildRequest({
270+
spec: sampleMultipartOpenApi3,
271+
operationId: 'post_land_content_uploadImage',
272+
requestBody: {
273+
imageId: 'id',
274+
'images[]': [file1, file2],
275+
},
276+
});
277+
278+
test('should return FormData entry list and item entries (in order)', async () => {
279+
expect(req).toMatchObject({
280+
method: 'POST',
281+
url: '/api/v1/land/content/uploadImage',
282+
credentials: 'same-origin',
283+
headers: {
284+
'Content-Type': expect.stringMatching(/^multipart\/form-data/),
285+
},
286+
});
287+
expect(req.body).toBeInstanceOf(Readable);
288+
const itemEntries = req.formdata.getAll('images[]');
289+
290+
expect(itemEntries.length).toEqual(2);
291+
expect(await itemEntries[0].text()).toEqual(await file1.text());
292+
expect(await itemEntries[1].text()).toEqual(await file2.text());
293+
});
294+
});
295+
260296
describe('respect Encoding Object', () => {
261297
test('Should be set to object in the style of deepObject', () => {
262298
const spec = {

test/http/index.js

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import xmock from 'xmock';
22
import fetchMock from 'fetch-mock';
3+
import { File, Blob } from 'formdata-node';
34

45
import http, {
56
serializeHeaders,
@@ -440,20 +441,15 @@ describe('http', () => {
440441
});
441442

442443
describe('isFile', () => {
443-
// mock browser File class
444-
global.File = class MockBrowserFile {};
445-
// mock browser Blob class
446-
global.Blob = class MockBrowserBlob {};
447-
448444
const mockBrowserNavigator = {
449445
product: 'Gecko',
450446
};
451447
const mockReactNativeNavigator = {
452448
product: 'ReactNative',
453449
};
454450

455-
const browserFile = new global.File();
456-
const browserBlob = new global.File();
451+
const browserFile = new File(['file'], 'file.txt');
452+
const browserBlob = new Blob(['blob']);
457453
const reactNativeFileObject = {
458454
uri: '/mock/path',
459455
};

0 commit comments

Comments
 (0)