Skip to content

Commit fbaed97

Browse files
authored
Update/Fix form-data/x-www-form-urlencoded snippets (#4)
* node-axios update application/form-data and x-www-form-urlencoded * node axios minor touches, node fetch placeholder path to file * javascript axios multipart/form-data remove content-type header and handle file param as a file instead of base64 * case-insensitive removeProperty helper(currently used to remove "content-type" header) * typo * node-fetch - remove 'content-type' header and add spread formData headers on multipart/form-data * update js-axios content-type header comment * reusable params helper - construct params code, checkIfRequestContainsFile general helper, use URLSearchParams for x-www-form-urlencoded, consistent code for node/js axios and fetch * "encodedParams" also in js axios * codeBuilder clone, constructAppendedParamsCode same "append" method + options argument * new helpers unit tests, mocharc file(to take only spec files) * insensitive case property remove test * use lodash isObject, check constructAppendedParamsCode test code result string * constructAppendedParamsCode options argument tests
1 parent 6e4a67e commit fbaed97

File tree

10 files changed

+378
-140
lines changed

10 files changed

+378
-140
lines changed

.mocharc.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"diff": true,
3+
"extension": ["js", "cjs", "mjs"],
4+
"package": "./package.json",
5+
"reporter": "spec",
6+
"slow": "75",
7+
"timeout": "2000",
8+
"ui": "bdd",
9+
"spec": "**/*.spec.js"
10+
}

package.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
},
5454
"scripts": {
5555
"quick": "mocha --no-timeouts --fgrep 'Request Validation' --invert",
56-
"pretest": "standard && echint",
5756
"test": "mocha --no-timeouts",
5857
"posttest": "exit 0 && npm run coverage",
5958
"coverage": "istanbul cover --dir coverage _mocha -- --fgrep 'Request Validation' --invert -R dot",
@@ -95,4 +94,4 @@
9594
"pinkie-promise": "^2.0.0",
9695
"stringify-object": "^3.3.0"
9796
}
98-
}
97+
}

src/helpers/code-builder.js

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1-
'use strict'
1+
"use strict";
22

3-
const util = require('util')
3+
const { cloneDeep } = require("lodash");
4+
const util = require("util");
45

56
/**
67
* Helper object to format and aggragate lines of code.
@@ -12,10 +13,10 @@ const util = require('util')
1213
* @param {string} join Desired character to join each line of code
1314
*/
1415
const CodeBuilder = function (indentation, join) {
15-
this.code = []
16-
this.indentation = indentation
17-
this.lineJoin = join || '\n'
18-
}
16+
this.code = [];
17+
this.indentation = indentation;
18+
this.lineJoin = join || "\n";
19+
};
1920

2021
/**
2122
* Add given indentation level to given string and format the string (variadic)
@@ -37,26 +38,26 @@ const CodeBuilder = function (indentation, join) {
3738
* // returns: 'console.log("\t\thello world")'
3839
*/
3940
CodeBuilder.prototype.buildLine = function (indentationLevel, line) {
40-
let lineIndentation = ''
41-
let slice = 2
42-
if (Object.prototype.toString.call(indentationLevel) === '[object String]') {
43-
slice = 1
44-
line = indentationLevel
45-
indentationLevel = 0
41+
let lineIndentation = "";
42+
let slice = 2;
43+
if (Object.prototype.toString.call(indentationLevel) === "[object String]") {
44+
slice = 1;
45+
line = indentationLevel;
46+
indentationLevel = 0;
4647
} else if (indentationLevel === null) {
47-
return null
48+
return null;
4849
}
4950

5051
while (indentationLevel) {
51-
lineIndentation += this.indentation
52-
indentationLevel--
52+
lineIndentation += this.indentation;
53+
indentationLevel--;
5354
}
5455

55-
const format = Array.prototype.slice.call(arguments, slice, arguments.length)
56-
format.unshift(lineIndentation + line)
56+
const format = Array.prototype.slice.call(arguments, slice, arguments.length);
57+
format.unshift(lineIndentation + line);
5758

58-
return util.format.apply(this, format)
59-
}
59+
return util.format.apply(this, format);
60+
};
6061

6162
/**
6263
* Invoke buildLine() and add the line at the top of current lines
@@ -65,10 +66,10 @@ CodeBuilder.prototype.buildLine = function (indentationLevel, line) {
6566
* @return {this}
6667
*/
6768
CodeBuilder.prototype.unshift = function () {
68-
this.code.unshift(this.buildLine.apply(this, arguments))
69+
this.code.unshift(this.buildLine.apply(this, arguments));
6970

70-
return this
71-
}
71+
return this;
72+
};
7273

7374
/**
7475
* Invoke buildLine() and add the line at the bottom of current lines
@@ -77,27 +78,35 @@ CodeBuilder.prototype.unshift = function () {
7778
* @return {this}
7879
*/
7980
CodeBuilder.prototype.push = function () {
80-
this.code.push(this.buildLine.apply(this, arguments))
81+
this.code.push(this.buildLine.apply(this, arguments));
8182

82-
return this
83-
}
83+
return this;
84+
};
8485

8586
/**
8687
* Add an empty line at the end of current lines
8788
* @return {this}
8889
*/
8990
CodeBuilder.prototype.blank = function () {
90-
this.code.push(null)
91+
this.code.push(null);
9192

92-
return this
93-
}
93+
return this;
94+
};
9495

9596
/**
9697
* Concatenate all current lines using the given lineJoin
9798
* @return {string}
9899
*/
99100
CodeBuilder.prototype.join = function () {
100-
return this.code.join(this.lineJoin)
101-
}
101+
return this.code.join(this.lineJoin);
102+
};
102103

103-
module.exports = CodeBuilder
104+
CodeBuilder.prototype.clone = function () {
105+
return cloneDeep(this);
106+
};
107+
108+
CodeBuilder.prototype.getLength = function () {
109+
return this.code.length;
110+
};
111+
112+
module.exports = CodeBuilder;

src/helpers/general.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const { omit, isObject } = require("lodash");
2+
3+
module.exports = {
4+
/**
5+
*
6+
* @param {Object} originalObject - The object from which the property needs to be removed
7+
* @param {string} propertyName - The name of the property to remove(case insensitive)
8+
* @returns the object without the property that was asked to remove
9+
*/
10+
removeProperty: (originalObject, propertyName) => {
11+
if (!isObject(originalObject)) {
12+
throw new Error("originalObject must be an object.");
13+
}
14+
const key = Object.keys(originalObject).find(
15+
(key) => key.toLowerCase() === propertyName.toLowerCase()
16+
);
17+
if (key) {
18+
return omit(originalObject, key);
19+
} else {
20+
return originalObject;
21+
}
22+
},
23+
24+
checkIfRequestContainsFile: (request) => {
25+
return (
26+
request.postData.mimeType === "multipart/form-data" &&
27+
request.postData.params.some((param) => param.fileName)
28+
);
29+
},
30+
};

src/helpers/helpers.spec.js

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
const { omit } = require("lodash");
2+
const should = require("should");
3+
const CodeBuilder = require("./code-builder");
4+
5+
const { removeProperty } = require("./general");
6+
const { constructAppendedParamsCode } = require("./params");
7+
8+
describe("Test helpers methods", () => {
9+
describe("Test RemoveProperty helper", () => {
10+
it("RemoveProperty called with invalid params", () => {
11+
(function () {
12+
removeProperty("str", "property");
13+
}.should.throw(new Error("originalObject must be an object.")));
14+
});
15+
16+
it("returned object stayed the same if a non existing property name sent", () => {
17+
const obj = { a: 1, b: 2 };
18+
const result = removeProperty(obj, "c");
19+
20+
result.should.equal(obj);
21+
});
22+
23+
it("insensitive case property removed from object successfully", () => {
24+
const obj = { a: 1, b: 2 };
25+
const result = removeProperty(obj, "B");
26+
27+
result.should.deepEqual(omit(obj, "b"));
28+
});
29+
});
30+
31+
describe("Test constructAppendedParamsCode helper", () => {
32+
const fakeParams = [
33+
{ name: "a", value: "1" },
34+
{ name: "b", value: "2" },
35+
];
36+
37+
it("called with invalid code argument", () => {
38+
(function () {
39+
constructAppendedParamsCode({}, []);
40+
}.should.throw(
41+
new Error("code argument must be an instance of CodeBuilder")
42+
));
43+
});
44+
45+
it("called with invalid params argument", () => {
46+
(function () {
47+
constructAppendedParamsCode(new CodeBuilder(), {});
48+
}.should.throw(new Error("params argument must be an array")));
49+
});
50+
51+
describe("called with multiple options variations", () => {
52+
const fakeParamsWithFile = [
53+
...fakeParams,
54+
{ name: "a", fileName: "fakeFileName" },
55+
];
56+
const lastIndex = params.length - 1;
57+
58+
it("called with file param and false isBrowser option", () => {
59+
const result = constructAppendedParamsCode(
60+
new CodeBuilder(),
61+
fakeParamsWithFile,
62+
{
63+
isBrowser: false,
64+
}
65+
);
66+
67+
result.should.be.an.instanceof(CodeBuilder);
68+
result
69+
.join()
70+
.should.containEql(
71+
`fs.createReadStream("/PATH/TO/${fakeParamsWithFile[lastIndex].fileName}")`
72+
);
73+
});
74+
75+
it("called with file param and true isBrowser option", () => {
76+
const result = constructAppendedParamsCode(
77+
new CodeBuilder(),
78+
fakeParamsWithFile,
79+
{
80+
isBrowser: true,
81+
}
82+
);
83+
84+
result.should.be.an.instanceof(CodeBuilder);
85+
result
86+
.join()
87+
.should.containEql(
88+
`yourAppInput.files[0], ${JSON.stringify(
89+
params[lastIndex].fileName
90+
)}`
91+
);
92+
});
93+
94+
it("called with dataVarName option", () => {
95+
const result = constructAppendedParamsCode(new CodeBuilder(), params, {
96+
dataVarName: "dataObject",
97+
});
98+
99+
result.should.be.an.instanceof(CodeBuilder);
100+
result.join().should.containEql("dataObject.append");
101+
});
102+
});
103+
104+
it("returned new code object with two params", () => {
105+
const result = constructAppendedParamsCode(new CodeBuilder(), fakeParams);
106+
107+
result.should.be.an.instanceof(CodeBuilder);
108+
result.getLength().should.equal(2);
109+
result
110+
.join()
111+
.should.equal('data.append("a", "1");\ndata.append("b", "2");');
112+
});
113+
});
114+
});

src/helpers/params.js

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
const { isArray } = require("lodash");
2+
3+
const CodeBuilder = require("./code-builder");
4+
5+
const defaultConstructParamsCodeOptions = {
6+
isBrowser: false,
7+
dataVarName: "data",
8+
};
9+
10+
module.exports = {
11+
/**
12+
*
13+
* @param {CodeBuilder} code - Original codeBuilder instance
14+
* @param {[Object]} params - List of params objects
15+
* * @param {Objects} options
16+
* @param {boolean} options.isBrowser - Boolean indicating if browser or other environment(e.g. node)
17+
* @param {string} options.dataVarName - The data object name
18+
* @returns New code instance with params appended to the supplied data object
19+
*/
20+
constructAppendedParamsCode: (
21+
code,
22+
params = [],
23+
options = defaultConstructParamsCodeOptions
24+
) => {
25+
if (!(code instanceof CodeBuilder)) {
26+
throw new Error("code argument must be an instance of CodeBuilder");
27+
} else if (!isArray(params)) {
28+
throw new Error("params argument must be an array");
29+
}
30+
31+
const { isBrowser = false, dataVarName = "data" } = options;
32+
const newCode = code.clone();
33+
34+
params.forEach(function (param) {
35+
let value =
36+
param.value !== undefined ? JSON.stringify(param.value.toString()) : "";
37+
if (param.fileName) {
38+
value = isBrowser
39+
? `yourAppInput.files[0], ${JSON.stringify(param.fileName)}`
40+
: `fs.createReadStream("/PATH/TO/${param.fileName}")`;
41+
}
42+
newCode.push(
43+
"%s.append(%s, %s);",
44+
dataVarName,
45+
JSON.stringify(param.name),
46+
value
47+
);
48+
});
49+
50+
return newCode;
51+
},
52+
};

0 commit comments

Comments
 (0)