Skip to content

Commit 456bd21

Browse files
authored
Merge pull request #600 from stripe/ob-multipart-flatten
Flatten parameters in multipart requests
2 parents 4d03a67 + 3cb25ee commit 456bd21

File tree

5 files changed

+91
-1
lines changed

5 files changed

+91
-1
lines changed

lib/MultipartDataGenerator.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict';
22

33
var Buffer = require('safe-buffer').Buffer;
4+
var utils = require('./utils');
45

56
// Method for formatting HTTP body for the multipart/form-data specification
67
// Mostly taken from Fermata.js
@@ -23,7 +24,7 @@ function multipartDataGenerator(method, data, headers) {
2324
return '"' + s.replace(/"|"/g, '%22').replace(/\r\n|\r|\n/g, ' ') + '"';
2425
}
2526

26-
for (var k in data) {
27+
for (var k in utils.flattenAndStringify(data)) {
2728
var v = data[k];
2829
push('--' + segno);
2930
if (v.hasOwnProperty('data')) {

lib/utils.js

+35
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,41 @@ var utils = module.exports = {
250250

251251
// For mocking in tests.
252252
_exec: exec,
253+
254+
isObject: function isObject(obj) {
255+
var type = typeof obj;
256+
return (type === 'function' || type === 'object') && !!obj;
257+
},
258+
259+
// For use in multipart requests
260+
flattenAndStringify: function flattenAndStringify(data) {
261+
var result = {};
262+
263+
function step(obj, prevKey) {
264+
Object.keys(obj).forEach(function (key) {
265+
var value = obj[key];
266+
267+
var newKey = prevKey ? `${prevKey}[${key}]` : key;
268+
269+
if (utils.isObject(value)) {
270+
if (!Buffer.isBuffer(value) && !value.hasOwnProperty('data')) {
271+
// Non-buffer non-file Objects are recursively flattened
272+
return step(value, newKey);
273+
} else {
274+
// Buffers and file objects are stored without modification
275+
result[newKey] = value;
276+
}
277+
} else {
278+
// Primitives are converted to strings
279+
result[newKey] = String(value);
280+
}
281+
})
282+
}
283+
284+
step(data);
285+
286+
return result;
287+
},
253288
};
254289

255290
function emitWarning(warning) {

test/resources/FileUploads.spec.js

+4
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ describe('File Uploads Resource', function() {
5555
name: 'minimal.pdf',
5656
type: 'application/octet-stream',
5757
},
58+
file_link_data: {create: true},
5859
});
5960

6061
expect(stripe.LAST_REQUEST).to.deep.property('host', 'files.stripe.com');
@@ -73,6 +74,7 @@ describe('File Uploads Resource', function() {
7374
name: 'minimal.pdf',
7475
type: 'application/octet-stream',
7576
},
77+
file_link_data: {create: true},
7678
}, TEST_AUTH_KEY);
7779

7880
expect(stripe.LAST_REQUEST).to.deep.property('host', 'files.stripe.com');
@@ -92,6 +94,7 @@ describe('File Uploads Resource', function() {
9294
name: 'minimal.pdf',
9395
type: 'application/octet-stream',
9496
},
97+
file_link_data: {create: true},
9598
}).then(function() {
9699
expect(stripe.LAST_REQUEST).to.deep.property('host', 'files.stripe.com');
97100
expect(stripe.LAST_REQUEST).to.deep.property('method', 'POST');
@@ -110,6 +113,7 @@ describe('File Uploads Resource', function() {
110113
name: 'minimal.pdf',
111114
type: 'application/octet-stream',
112115
},
116+
file_link_data: {create: true},
113117
}, TEST_AUTH_KEY).then(function() {
114118
expect(stripe.LAST_REQUEST).to.deep.property('host', 'files.stripe.com');
115119
expect(stripe.LAST_REQUEST).to.deep.property('method', 'POST');

test/resources/Files.spec.js

+4
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ describe('Files Resource', function() {
5555
name: 'minimal.pdf',
5656
type: 'application/octet-stream',
5757
},
58+
file_link_data: {create: true},
5859
});
5960

6061
expect(stripe.LAST_REQUEST).to.deep.property('host', 'files.stripe.com');
@@ -73,6 +74,7 @@ describe('Files Resource', function() {
7374
name: 'minimal.pdf',
7475
type: 'application/octet-stream',
7576
},
77+
file_link_data: {create: true},
7678
}, TEST_AUTH_KEY);
7779

7880
expect(stripe.LAST_REQUEST).to.deep.property('host', 'files.stripe.com');
@@ -92,6 +94,7 @@ describe('Files Resource', function() {
9294
name: 'minimal.pdf',
9395
type: 'application/octet-stream',
9496
},
97+
file_link_data: {create: true},
9598
}).then(function() {
9699
expect(stripe.LAST_REQUEST).to.deep.property('host', 'files.stripe.com');
97100
expect(stripe.LAST_REQUEST).to.deep.property('method', 'POST');
@@ -110,6 +113,7 @@ describe('Files Resource', function() {
110113
name: 'minimal.pdf',
111114
type: 'application/octet-stream',
112115
},
116+
file_link_data: {create: true},
113117
}, TEST_AUTH_KEY).then(function() {
114118
expect(stripe.LAST_REQUEST).to.deep.property('host', 'files.stripe.com');
115119
expect(stripe.LAST_REQUEST).to.deep.property('method', 'POST');

test/utils.spec.js

+46
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ require('../testUtils');
44

55
var utils = require('../lib/utils');
66
var expect = require('chai').expect;
7+
var Buffer = require('safe-buffer').Buffer;
78

89
describe('utils', function() {
910
describe('makeURLInterpolator', function() {
@@ -338,6 +339,51 @@ describe('utils', function() {
338339
]);
339340
});
340341
})
342+
343+
describe('flattenAndStringify', function() {
344+
it('Stringifies primitive types', function() {
345+
expect(utils.flattenAndStringify({
346+
a: 1,
347+
b: 'foo',
348+
c: true,
349+
d: null,
350+
})).to.eql({'a': '1', 'b': 'foo', 'c': 'true', 'd': 'null'});
351+
});
352+
353+
it('Flattens nested values', function() {
354+
expect(utils.flattenAndStringify({
355+
x: {
356+
a: 1,
357+
b: 'foo',
358+
},
359+
})).to.eql({'x[a]': '1', 'x[b]': 'foo'});
360+
});
361+
362+
it('Does not flatten File objects', function() {
363+
expect(utils.flattenAndStringify({
364+
file: {
365+
data: 'foo'
366+
},
367+
x: {
368+
a: 1,
369+
},
370+
})).to.eql({'file': {data: 'foo'}, 'x[a]': '1'});
371+
});
372+
373+
it('Does not flatten Buffer objects', function() {
374+
var buf = Buffer.from('Hi!');
375+
var flattened = utils.flattenAndStringify({
376+
buf: buf,
377+
x: {
378+
a: 1,
379+
},
380+
});
381+
expect(flattened).to.have.property('buf');
382+
expect(flattened.buf).to.deep.equal(buf);
383+
expect(flattened).to.have.property('x[a]');
384+
expect(flattened['x[a]']).to.equal('1');
385+
});
386+
});
341387
});
342388

343389
function handleWarnings(doWithShimmedConsoleWarn, onWarn) {

0 commit comments

Comments
 (0)