ft: utils.createObjWithHashedKeys ft: curlify.extractKey test: curlify with array representationbubble
@@ -1,5 +1,19 @@ | |||
import win from "./window" | |||
/** | |||
* if duplicate key name existed from FormData entries, | |||
* we mutated the key name by appending a hashIdx | |||
* @param {String} k - possibly mutated key name | |||
* @return {String} - src key name | |||
*/ | |||
const extractKey = (k) => { | |||
const hashIdx = "_**[]" | |||
if (k.indexOf(hashIdx) < 0) { | |||
return k | |||
} | |||
return k.split(hashIdx)[0].trim() | |||
} | |||
export default function curl( request ){ | |||
let curlified = [] | |||
let type = "" | |||
@@ -21,11 +35,12 @@ export default function curl( request ){ | |||
if(type === "multipart/form-data" && request.get("method") === "POST") { | |||
for( let [ k,v ] of request.get("body").entrySeq()) { | |||
let extractedKey = extractKey(k) | |||
curlified.push( "-F" ) | |||
if (v instanceof win.File) { | |||
curlified.push( `"${k}=@${v.name}${v.type ? `;type=${v.type}` : ""}"` ) | |||
curlified.push(`"${extractedKey}=@${v.name}${v.type ? `;type=${v.type}` : ""}"` ) | |||
} else { | |||
curlified.push( `"${k}=${v}"` ) | |||
curlified.push(`"${extractedKey}=${v}"` ) | |||
} | |||
} | |||
} else { | |||
@@ -67,17 +67,64 @@ export function arrayify (thing) { | |||
return normalizeArray(thing) | |||
} | |||
export function fromJSOrdered (js) { | |||
if(isImmutable(js)) | |||
export function fromJSOrdered(js) { | |||
if (isImmutable(js)) { | |||
return js // Can't do much here | |||
if (js instanceof win.File) | |||
} | |||
if (js instanceof win.File) { | |||
return js | |||
} | |||
if (!isObject(js)) { | |||
return js | |||
} | |||
if (Array.isArray(js)) { | |||
return Im.Seq(js).map(fromJSOrdered).toList() | |||
} | |||
if (js.entries) { | |||
// handle multipart/form-data | |||
const objWithHashedKeys = createObjWithHashedKeys(js) | |||
return Im.OrderedMap(objWithHashedKeys).map(fromJSOrdered) | |||
} | |||
return Im.OrderedMap(js).map(fromJSOrdered) | |||
} | |||
return !isObject(js) ? js : | |||
Array.isArray(js) ? | |||
Im.Seq(js).map(fromJSOrdered).toList() : | |||
Im.OrderedMap(js).map(fromJSOrdered) | |||
/** | |||
* Convert a FormData object into plain object | |||
* Append a hashIdx and counter to the key name, if multiple exists | |||
* if single, key name = <original> | |||
* if multiple, key name = <original><hashIdx><count> | |||
* @param {FormData} fdObj - a FormData object | |||
* @return {Object} - a plain object | |||
*/ | |||
export function createObjWithHashedKeys (fdObj) { | |||
if (!fdObj.entries) { | |||
return fdObj // not a FormData object with iterable | |||
} | |||
const newObj = {} | |||
const hashIdx = "_**[]" // our internal identifier | |||
const trackKeys = {} | |||
for (let pair of fdObj.entries()) { | |||
if (!newObj[pair[0]] && !(trackKeys[pair[0]] && trackKeys[pair[0]].containsMultiple)) { | |||
newObj[pair[0]] = pair[1] // first key name: no hash required | |||
} else { | |||
if (!trackKeys[pair[0]]) { | |||
// initiate tracking key for multiple | |||
trackKeys[pair[0]] = { | |||
containsMultiple: true, | |||
length: 1 | |||
} | |||
// "reassign" first pair to matching hashed format for multiple | |||
let hashedKeyFirst = `${pair[0]}${hashIdx}${trackKeys[pair[0]].length}` | |||
newObj[hashedKeyFirst] = newObj[pair[0]] | |||
// remove non-hashed key of multiple | |||
delete newObj[pair[0]] // first | |||
} | |||
trackKeys[pair[0]].length += 1 | |||
let hashedKeyCurrent = `${pair[0]}${hashIdx}${trackKeys[pair[0]].length}` | |||
newObj[hashedKeyCurrent] = pair[1] | |||
} | |||
} | |||
return newObj | |||
} | |||
export function bindToState(obj, state) { | |||
@@ -143,6 +143,26 @@ describe("curlify", function() { | |||
expect(curlified).toEqual("curl -X POST \"http://example.com\" -H \"content-type: multipart/form-data\" -F \"id=123\" -F \"name=Sahar\"") | |||
}) | |||
it("should print a curl with formData that extracts array representation with hashIdx", function() { | |||
// Note: hashIdx = `_**[]${counter}` | |||
// Usage of hashIdx is an internal SwaggerUI method to convert formData array into something curlify can handle | |||
const req = { | |||
url: "http://example.com", | |||
method: "POST", | |||
headers: { "content-type": "multipart/form-data" }, | |||
body: { | |||
id: "123", | |||
"fruits[]_**[]1": "apple", | |||
"fruits[]_**[]2": "banana", | |||
"fruits[]_**[]3": "grape" | |||
} | |||
} | |||
let curlified = curl(Im.fromJS(req)) | |||
expect(curlified).toEqual("curl -X POST \"http://example.com\" -H \"content-type: multipart/form-data\" -F \"id=123\" -F \"fruits[]=apple\" -F \"fruits[]=banana\" -F \"fruits[]=grape\"") | |||
}) | |||
it("should print a curl with formData and file", function() { | |||
var file = new win.File() | |||
file.name = "file.txt" | |||