Skip to content

Commit

Permalink
feat: add support for passing JSON/raw body from a file
Browse files Browse the repository at this point in the history
ctn-malone committed Sep 27, 2021
1 parent abfcb1d commit 75e3f6d
Showing 3 changed files with 79 additions and 9 deletions.
22 changes: 18 additions & 4 deletions doc/curl.md
Original file line number Diff line number Diff line change
@@ -29,18 +29,25 @@ Constructor
* content type will automatically be set to application/json
* will be ignored unless `opt.method` is one of `["PUT", "POST", "DELETE", "PATCH"]`
* will be ignored if `opt.data` was set
* opt.jsonFile (*string*) : file containing the data to send as `application/json`
* content type will automatically be set to application/json
* will be ignored unless `opt.method` is one of `["PUT", "POST", "DELETE", "PATCH"]`
* will be ignored if one of (`opt.data`, `opt.json`) was set
* opt.file (*string|object*) : used to upload a file
* content type will automatically be set to `multipart/form-data`
* will be ignored unless `opt.method` is one of `["PUT", "POST", "DELETE", "PATCH"]`
* will be ignored if one of (`opt.data`, `opt.json`) was set
* will be ignored if one of (`opt.data`, `opt.json`, `opt.jsonFile`) was set
* when using a *string*, `opt.file` should be the path of the file to upload
* when using an *object*
* **[opt.file.filepath]** (*string*) : path of the local file (mandatory)
* opt.file.name (*string*) : name of the form parameter (default = `file`)
* opt.file.filename (*string*) : name of the file (defaults to the name of the local file)
* opt.body (*string*) : raw body to send
* will be ignored unless `opt.method` is one of `["PUT", "POST", "DELETE", "PATCH"]`
* will be ignored if one of (`opt.data`, `opt.json`, `opt.file`) was set
* will be ignored if one of (`opt.data`, `opt.json`, `opt.jsonFile`, `opt.file`) was set
* opt.bodyFile (*string*) : file containing the raw body to send
* will be ignored unless `opt.method` is one of `["PUT", "POST", "DELETE", "PATCH"]`
* will be ignored if one of (`opt.data`, `opt.json`, `opt.jsonFile`, `opt.file`, `opt.body`) was set
* opt.params (*object*) : parameters to add as query string
* opt.normalizeHeaders (*boolean*) : if `true`, header names in response will be converted to lower case (default = `true`)
* opt.parseJson (*boolean*) : if `true`, automatically parse JSON in responses (default = `true`)
@@ -441,18 +448,25 @@ Perfoms a *curl* request and return the response's body
* content type will automatically be set to application/json
* will be ignored unless `opt.method` is one of `["PUT", "POST", "DELETE", "PATCH"]`
* will be ignored if `opt.data` was set
* opt.jsonFile (*string*) : file containing the data to send as `application/json`
* content type will automatically be set to application/json
* will be ignored unless `opt.method` is one of `["PUT", "POST", "DELETE", "PATCH"]`
* will be ignored if one of (`opt.data`, `opt.json`) was set
* opt.file (*string|object*) : used to upload a file
* content type will automatically be set to `multipart/form-data`
* will be ignored unless `opt.method` is one of `["PUT", "POST", "DELETE", "PATCH"]`
* will be ignored if one of (`opt.data`, `opt.json`) was set
* will be ignored if one of (`opt.data`, `opt.json`, `opt.jsonFile`) was set
* when using a *string*, `opt.file` should be the path of the file to upload
* when using an *object*
* **[opt.file.filepath]** (*string*) : path of the local file (mandatory)
* opt.file.name (*string*) : name of the form parameter (default = `file`)
* opt.file.filename (*string*) : name of the file (defaults to the name of the local file)
* opt.body (*string*) : raw body to send
* will be ignored unless `opt.method` is one of `["PUT", "POST", "DELETE", "PATCH"]`
* will be ignored if one of (`opt.data`, `opt.json`, `opt.file`) was set
* will be ignored if one of (`opt.data`, `opt.json`, `opt.jsonFile`, `opt.file`) was set
* opt.bodyFile (*string*) : file containing the raw body to send
* will be ignored unless `opt.method` is one of `["PUT", "POST", "DELETE", "PATCH"]`
* will be ignored if one of (`opt.data`, `opt.json`, `opt.jsonFile`, `opt.file`, `opt.body`) was set
* opt.params (*object*) : parameters to add as query string
* opt.normalizeHeaders (*boolean*) : if `true`, header names in response will be converted to lower case (default = `true`)
* opt.parseJson (*boolean*) : if `true`, automatically parse JSON in responses (default = `true`)
38 changes: 33 additions & 5 deletions src/curl.js
Original file line number Diff line number Diff line change
@@ -35,17 +35,24 @@ class Curl {
* Content type will automatically be set to application/json
* Will be ignored unless {opt.method} is one of ("PUT", "POST", "DELETE", "PATCH")
* Will be ignored if {opt.data} was set
* @param {object|string|true} opt.jsonFile file containing data to send as application/json
* Content type will automatically be set to application/json
* Will be ignored unless {opt.method} is one of ("PUT", "POST", "DELETE", "PATCH")
* Will be ignored if one of ({opt.data}, {opt.json}) was set
* @param {string|object} opt.file used to upload a file
* Content type will automatically be set to multipart/form-data
* Will be ignored unless {opt.method} is one of ("PUT", "POST", "DELETE", "PATCH")
* Will be ignored if one of ({opt.data}, {opt.json}) was set
* Will be ignored if one of ({opt.data}, {opt.json}, {opt.jsonFile}) was set
* When using a {string}, {opt.file} should be the path of the file to upload
* @param {string} opt.file.filepath path of the local file (mandatory)
* @param {string} opt.file.name name of the form parameter (default = {"file"})
* @param {string} opt.file.filename name of the file (defaults to the name of the local file)
* @param {string} opt.body body to send
* @param {string} opt.body file containing the body to send
* Will be ignored unless {opt.method} is one of ("PUT", "POST", "DELETE", "PATCH")
* Will be ignored if one of ({opt.data}, {opt.json}, {opt.file}) was set
* Will be ignored if one of ({opt.data}, {opt.json}, {opt.jsonFile}, {opt.file}) was set
* @param {string} opt.bodyFile body to send
* Will be ignored unless {opt.method} is one of ("PUT", "POST", "DELETE", "PATCH")
* Will be ignored if one of ({opt.data}, {opt.json}, {opt.jsonFile}, {opt.file}, {opt.body}) was set
* @param {object} opt.params parameters to add as query string
* @param {boolean} opt.normalizeHeaders if {true}, header names in response will be converted to lower case (default = {true})
* @param {boolean} opt.parseJson if {true}, automatically parse JSON in responses (default = {true})
@@ -230,6 +237,15 @@ class Curl {
}
}
}
// json body from json file
else if (undefined !== opt.jsonFile) {
if ('application/json' != contentType) {
this._curlArgs.push('-H');
this._curlArgs.push(`Content-Type: application/json`);
}
this._curlArgs.push('-d');
this._curlArgs.push(`@${opt.jsonFile}`);
}
else if (undefined !== opt.file) {
/*
Content-Type will be set automatically to
@@ -268,6 +284,11 @@ class Curl {
// no need to escape anything since we're using exec
this._curlArgs.push(opt.body);
}
// raw content from file
else if (undefined !== opt.bodyFile) {
this._curlArgs.push('-d');
this._curlArgs.push(`@${opt.bodyFile}`);
}
}
// url & query string
let finalUrl = url;
@@ -755,17 +776,24 @@ class Curl {
* Content type will automatically be set to application/json
* Will be ignored unless {opt.method} is one of ("PUT", "POST", "DELETE", "PATCH")
* Will be ignored if {opt.data} was set
* @param {object|string|true} opt.jsonFile file containing data to send as application/json
* Content type will automatically be set to application/json
* Will be ignored unless {opt.method} is one of ("PUT", "POST", "DELETE", "PATCH")
* Will be ignored if one of ({opt.data}, {opt.json}) was set
* @param {string|object} opt.file used to upload a file
* Content type will automatically be set to multipart/form-data
* Will be ignored unless {opt.method} is one of ("PUT", "POST", "DELETE", "PATCH")
* Will be ignored if one of ({opt.data}, {opt.json}) was set
* Will be ignored if one of ({opt.data}, {opt.json}, {opt.jsonFile}) was set
* When using a {string}, {opt.file} should be the path of the file to upload
* @param {string} opt.file.filepath path of the local file (mandatory)
* @param {string} opt.file.name name of the form parameter (default = {"file"})
* @param {string} opt.file.filename name of the file (defaults to the name of the local file)
* @param {string} opt.body body to send
* Will be ignored unless {opt.method} is one of ("PUT", "POST", "DELETE", "PATCH")
* Will be ignored if one of ({opt.data}, {opt.json}, {opt.file}) was set
* Will be ignored if one of ({opt.data}, {opt.json}, {opt.jsonFile}, {opt.file}) was set
* @param {string} opt.bodyFile body to send
* Will be ignored unless {opt.method} is one of ("PUT", "POST", "DELETE", "PATCH")
* Will be ignored if one of ({opt.data}, {opt.json}, {opt.jsonFile}, {opt.file}, {opt.body}) was set
* @param {object} opt.params parameters to add as query string
* @param {boolean} opt.normalizeHeaders if {true}, header names in response will be converted to lower case (default = {true})
* @param {boolean} opt.parseJson if {true}, automatically parse JSON in responses (default = {true})
28 changes: 28 additions & 0 deletions test/tests/test.curl.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import * as std from 'std';
import { tester } from '../../src/tester.js';
import { Curl, curlRequest, multiCurl } from '../../src/curl.js'

@@ -234,6 +235,16 @@ export default () => {
tester.assertEq(cmdline, expectedCmdline, `cmdline should not contain any data when using a non-string as raw body`);
});

tester.test('curl.Curl (send raw body from file)', () => {
let c = new Curl('http://127.0.0.1', {
method: 'post',
bodyFile:'body.txt'
});
let expectedCmdline = `curl -D /dev/stderr -q -X POST -L -d @body.txt --url http://127.0.0.1`;
let cmdline = c.cmdline;
tester.assertEq(cmdline, expectedCmdline, `cmdline should match when using a file to define raw body`);
});

tester.test('curl.Curl (send query parameters)', () => {
let c = new Curl('http://127.0.0.1', {
params:{
@@ -335,6 +346,23 @@ export default () => {
done();
}, {isAsync:true});

tester.test('curl.Curl (POST request from file)', async (done) => {
const jsonFile = 'data/post_json1.json';
const reqBody = JSON.parse(std.loadFile(jsonFile));
let c = new Curl('http://jsonplaceholder.typicode.com/posts', {
method:'post',
jsonFile:jsonFile
});
let result = await c.run();
tester.assert(result, `request should succeed`);
tester.assertEq(typeof c.body, 'object', `response payload should be an object`);
let resBody = c.body;
delete resBody.id;
tester.assertEq(resBody, reqBody, `response payload should be as expected`);

done();
}, {isAsync:true});

tester.test('curl.Curl (PUT request)', async (done) => {
let reqBody = {
id: 1,

0 comments on commit 75e3f6d

Please sign in to comment.