diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..99638d6 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +# add git-ignore syntax here of things you don't want copied into docker image + +.git +node_modules +lib +yarn-error.log \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..75e0525 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,33 @@ +FROM node:12 + +# Avoid warnings by switching to noninteractive +ENV DEBIAN_FRONTEND=noninteractive + +# Configure apt and install packages +RUN apt-get update \ + && apt-get -y install --no-install-recommends apt-utils dialog 2>&1 \ + # Remove outdated yarn from /opt and install via package + # so it can be easily updated via apt-get upgrade yarn + && rm -rf /opt/yarn-* \ + && rm -f /usr/local/bin/yarn \ + && rm -f /usr/local/bin/yarnpkg \ + && apt-get install -y curl apt-transport-https lsb-release \ + && curl -sS https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/pubkey.gpg | apt-key add - 2>/dev/null \ + && echo "deb https://dl.yarnpkg.com/$(lsb_release -is | tr '[:upper:]' '[:lower:]')/ stable main" | tee /etc/apt/sources.list.d/yarn.list \ + && apt-get update \ + && apt-get -y install --no-install-recommends yarn \ + # + # Install eslint globally + && npm install -g eslint \ + # Clean up + && apt-get autoremove -y \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* + +# Switch back to dialog for any ad-hoc use of apt-get +ENV DEBIAN_FRONTEND= + +WORKDIR /app +COPY . /app + +CMD yarn install && yarn test && yarn build \ No newline at end of file diff --git a/README.md b/README.md index 2170ef5..b33f137 100644 --- a/README.md +++ b/README.md @@ -72,6 +72,36 @@ const options = { httpToCurl(options); ``` + +## 3. Disable output 👁 +This flag defaults to `true` and displays the output `curl` commands. You may disable output from this library. +```js +const options = { + showOutput: false +} +httpToCurl(options); +``` + +# Building and using the library locally +To avoid the need for installing the necessary libraries, the build can be performed within a Docker container. + +### Prequisite +- Docker + +### Instructions +In the project root folder, run the following commands. +```bash +docker build -t http-to-curl . +docker run --name builder -it http-to-curl +docker cp builder:/app/lib . +docker container rm builder +``` + +You may now reference the project directly using +```js +require(''); +``` + ## Contributing -We'd ❤️ to have your helping hand on http-to-curl! Feel free to PR's, add issues or give feedback! Happy Hacking!! 😎 +We'd ❤️ to have your helping hand on http-to-curl! Feel free to PR's, add issues or give feedback! Happy Hacking!! 😎 \ No newline at end of file diff --git a/package.json b/package.json index db1cfb2..71df4e7 100644 --- a/package.json +++ b/package.json @@ -24,6 +24,7 @@ "babel-jest": "^22.4.3", "esm": "^3.0.27", "jest": "^22.4.3", + "nock": "^12.0.3", "regenerator-runtime": "^0.11.1", "rollup": "^0.58.2", "rollup-plugin-babel": "^3.0.4", diff --git a/src/main.js b/src/main.js index 3b000bf..4832d0a 100644 --- a/src/main.js +++ b/src/main.js @@ -38,7 +38,8 @@ export function generateHeader(options) { let headerParam = ''; Object.keys(headers).map((val, key) => { if (val.toLocaleLowerCase() !== 'content-length') { - headerParam += `-H "${val}: ${headers[val].replace(/(\\|")/g, '\\$1')}" `; + // contents of headers[val] is a , so we need to convert that back to a string + headerParam += `-H "${val}: ${headers[val].toString('utf8').replace(/(\\|")/g, '\\$1')}" `; } if (val.toLocaleLowerCase() === 'accept-encoding') { isEncode = true; @@ -59,7 +60,10 @@ export function generateHeader(options) { */ export function generateUrl(options = {}) { if (!options) return ''; - const { protocol = 'http:', hostname = 'localhost', pathname = '/' } = options; + var { protocol, hostname, pathname, uri } = options; + protocol = protocol || uri && uri.protocol || 'http:'; + hostname = hostname || uri && uri.hostname || 'localhost'; + pathname = pathname || uri && uri.pathname || '/'; return `"${protocol}//${hostname}${pathname}"`; } @@ -129,11 +133,21 @@ export function curlGenerator(options, body = '', regex) { * @param {any} cb * @returns */ -export function requestPatch(regex, request, options, cb, customCallback) { +export function requestPatch(regex, request, options, cb, customCallback, showOutput) { + // Note that options may be | | + // https://nodejs.org/api/https.html#https_https_request_url_options_callback + // How `https` handles the params: https://github.com/nodejs/node/blob/v12.x/lib/https.js#L281 + const bodyData = []; const clientReq = request(options, cb); monkeypatch(clientReq, 'write', (original, chunk, encoding, cb) => { + // `chunk` is expected to be | + // Convert into , because bodyData should be an array of + // https://nodejs.org/api/http.html#http_request_write_chunk_encoding_callback + if (typeof chunk === 'string' || chunk instanceof String) { + chunk = Buffer.from(chunk, 'utf8'); + } bodyData.push(chunk); return original(chunk, encoding, cb); }); @@ -148,9 +162,11 @@ export function requestPatch(regex, request, options, cb, customCallback) { } const command = curlGenerator(options, body, regex); - console.log(`${chalk.black.bgYellow.bold(' http-to-curl ')} - ${command} - `); + + if (showOutput){ + console.log(`${chalk.black.bgYellow.bold(' http-to-curl ')}\n ${command}\n`); + } + customCallback(command); return original(data, encoding, cb); }); @@ -173,10 +189,10 @@ function httpToCurl(options) { * @param {*} httpObject * @param {*} { filter = '', customCallback = () => {} } */ -function monkeyPatchHttp(httpObject, options = { filter: '', customCallback: () => {} }) { +function monkeyPatchHttp(httpObject, options = {}) { monkeypatch(httpObject, 'request', (request, requestOptions, cb) => { - const { filter, customCallback } = options; - return requestPatch(filter, request, requestOptions, cb, customCallback); + const { filter = '', customCallback = () => {}, showOutput = true } = options; + return requestPatch(filter, request, requestOptions, cb, customCallback, showOutput); }); } diff --git a/test/main.test.js b/test/main.test.js index d344e22..3f6547a 100644 --- a/test/main.test.js +++ b/test/main.test.js @@ -6,6 +6,65 @@ import { generateBody, generateCompress } from '../src/main'; +import httpToCurl from '../src/main'; + +const request = require('request'); +const nock = require('nock'); + + +describe('Request using options object', () => { + + const mock_callback = jest.fn(); + const httpToCurlOptions = { + customCallback: mock_callback, + showOutput: false, + }; + httpToCurl(httpToCurlOptions); + + beforeEach(() => { + nock('https://api.httpcurl.com') + .post('/users/data') + .reply(200, '{ "response": true}'); + }); + + it('POST form data', done => { + expect.assertions(7) + + //Configuration options for request + var options = { + method: 'POST', + url: 'https://api.httpcurl.com/users/data', + headers: { + 'Content-Type': ['application/x-www-form-urlencoded'], + 'header1': "value1", + }, + form: { + attrib1: 'attribvalue1', + attrib2: 'attribvalue2', + }, + }; + + request.post(options); + + //expect an object back + setTimeout(() => { + try { + expect(mock_callback).toBeCalled(); + const result = mock_callback.mock.calls[0][0]; // first call, first parameter + expect(result).toContain("https://api.httpcurl.com"); + expect(result).toContain("-X POST"); + expect(result).toContain("attrib1=attribvalue1&attrib2=attribvalue2"); + expect(result).toContain("Content-Type: application/x-www-form-urlencoded"); + expect(result).toContain("header1: value1"); + expect(result).toContain("host: api.httpcurl.com"); + done() + } catch (err) { + done.fail(err) + } + }) + + }); +}); describe('Generate method param', () => { test('No method', () => { diff --git a/yarn.lock b/yarn.lock index ebdecab..64ff8cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1293,6 +1293,13 @@ debug@^2.1.2, debug@^2.2.0, debug@^2.3.3, debug@^2.6.8: dependencies: ms "2.0.0" +debug@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.1.1.tgz#3b72260255109c6b589cee050f1d516139664791" + integrity sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw== + dependencies: + ms "^2.1.1" + decamelize@^1.1.1: version "1.2.0" resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" @@ -2519,7 +2526,7 @@ json-stable-stringify@^1.0.1: dependencies: jsonify "~0.0.0" -json-stringify-safe@~5.0.1: +json-stringify-safe@^5.0.1, json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -2610,7 +2617,7 @@ lodash.sortby@^4.7.0: version "4.7.0" resolved "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438" -lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.4, lodash@^4.2.0: +lodash@^4.13.1, lodash@^4.14.0, lodash@^4.17.13, lodash@^4.17.4, lodash@^4.2.0: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" @@ -2767,6 +2774,11 @@ ms@2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" +ms@^2.1.1: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + nan@^2.9.2: version "2.10.0" resolved "https://registry.npmjs.org/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" @@ -2804,6 +2816,16 @@ neo-async@^2.6.0: version "2.6.1" resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" +nock@^12.0.3: + version "12.0.3" + resolved "https://registry.yarnpkg.com/nock/-/nock-12.0.3.tgz#83f25076dbc4c9aa82b5cdf54c9604c7a778d1c9" + integrity sha512-QNb/j8kbFnKCiyqi9C5DD0jH/FubFGj5rt9NQFONXwQm3IPB0CULECg/eS3AU1KgZb/6SwUa4/DTRKhVxkGABw== + dependencies: + debug "^4.1.0" + json-stringify-safe "^5.0.1" + lodash "^4.17.13" + propagate "^2.0.0" + node-int64@^0.4.0: version "0.4.0" resolved "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" @@ -3109,6 +3131,11 @@ process-nextick-args@~2.0.0: version "2.0.0" resolved "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz#a37d732f4271b4ab1ad070d35508e8290788ffaa" +propagate@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" + integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== + pseudomap@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"