Skip to content

Commit

Permalink
Merge branch 'release/1.5.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
VShingala committed Mar 31, 2023
2 parents e5a84c2 + 9e2232f commit 218d7d9
Show file tree
Hide file tree
Showing 5 changed files with 303 additions and 11 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# cURL to Postman Importer Changelog

#### v1.5.0 (March 31, 2023)
* Fixed an issue where request generation failed for certain bash operators.
* Fixed an issue where cURL with comments described was converted incorrectly.

#### v1.4.0 (March 17, 2023)
* Fixed issue [#7895](https://github.com/postmanlabs/postman-app-support/issues/7895) where cURL with no specific method defined for formdata type of body were not converted correctly.

Expand Down
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "curl-to-postmanv2",
"version": "1.4.0",
"version": "1.5.0",
"description": "Convert a given CURL command to a Postman request",
"main": "index.js",
"com_postman_plugin": {
Expand Down
47 changes: 43 additions & 4 deletions src/lib.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ const commander = require('commander'),
shellQuote = require('../assets/shell-quote'),
unnecessaryOptions = require('../assets/unnecessaryOptions'),
supportedOptions = require('../assets/supportedOptions'),
formDataOptions = ['-d', '--data', '--data-raw', '--data-binary', '--data-ascii'];
formDataOptions = ['-d', '--data', '--data-raw', '--data-binary', '--data-ascii'],
allowedOperators = ['<', '>', '(', ')'];

var program,

Expand Down Expand Up @@ -122,8 +123,12 @@ var program,
' (-d/--data/--data-raw/--data-binary/--data-ascii/--data-urlencode) are not supported');
}

// must have a URL
if (curlObj.args.length > 1 && !curlObj.url) {
/**
* For cURL with ^ as line termination character, each such line termination char will be an separate arg.
* throw an error as we have separate handling for parsing such cURLs
* once it fails here using convertForCMDFormat()
*/
if (curlObj.args.length > 1 && _.includes(curlObj.args, '^')) {
throw new Error('Only the URL can be provided without an option preceding it.' +
' All other inputs must be specified via options.');
}
Expand Down Expand Up @@ -444,8 +449,36 @@ var program,
}
/* eslint-enable */
}
else if (curlObj.args.length > 0) {
let argStr = typeof curlObj.url === 'string' ? curlObj.url : '';

// eslint-disable-next-line consistent-return
_.forEach(curlObj.args, (arg, index) => {
const previousArgOp = _.get(curlObj.args, `${index - 1}.op`, ''),
shouldAddCurrentArg = index === 0 || allowedOperators.includes(previousArgOp);

if (typeof arg === 'string' && shouldAddCurrentArg) {
/**
* Add current string arg only if previous arg is an allowed op.
* For URL like "hello.com/<id>", as "<" and ">" are treated as bash operators,
* we'll add such operator and next arg that was split up by it in URL
*/
argStr += arg;
}
else if (typeof arg === 'object' && allowedOperators.includes(arg.op)) {
argStr += arg.op;
}
else {
/**
* Stop adding more args as soon as we know that args are not split by allowed operators.
*/
return false;
}
});
this.requestUrl = argStr;
}
else {
this.requestUrl = curlObj.args[0];
throw new Error('Could not detect the URL from cURL. Please make sure it\'s a valid cURL');
}
},

Expand Down Expand Up @@ -598,6 +631,12 @@ var program,
curlObj = program.parse(sanitizedArgs);
}

// Filter out comments from Args
curlObj.args = _.filter(curlObj.args, (arg) => {
// Each arg should be string itself, for comment we receive an object from parser
return !(typeof arg === 'object' && typeof arg.comment === 'string');
});

this.headerPairs = {};

// if method is not given in the curl command
Expand Down
259 changes: 254 additions & 5 deletions test/conversion.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ describe('Curl converter should', function() {
});
});

it('throw an error for a cURL without URL defined correctly', function (done) {
convert({
type: 'string',
data: 'curl -X POST -H \'Content-type: application/json\' #{reply_url} --data \'#{response.to_json}\''
}, function (err, result) {
expect(result.result).to.equal(false);
expect(result.reason).to.equal('Error while parsing cURL: Could not identify the URL.' +
' Please use the --url option.');
done();
});
});

it('[Github #7390]: set request URL correctly irrespective of where it is mentioned', function (done) {
convert({
type: 'string',
Expand Down Expand Up @@ -168,6 +180,23 @@ describe('Curl converter should', function() {
});
});

it('convert a simple request with comment at the end correctly', function (done) {
convert({
type: 'string',
data: 'curl --request GET --url http://www.google.com #comment1'
}, function (err, result) {
expect(result.result).to.equal(true);

expect(result.output.length).to.equal(1);
expect(result.output[0].type).to.equal('request');

var request = result.output[0].data;
expect(request.method).to.equal('GET');
expect(request.url).to.equal('http://www.google.com');
done();
});
});

it('convert a simple GET request', function (done) {
var result = Converter.convertCurlToRequest('curl --request GET --url http://www.google.com');
expect(result.method).to.equal('GET');
Expand Down Expand Up @@ -919,18 +948,22 @@ describe('Curl converter should', function() {
});
});

it('in case where there is a invalid character at the end it should throw an error', function(done) {
it('in case where there is a invalid character at the end it should safely generate request', function(done) {
convert({
type: 'string',
data: `curl --location --request POST \\
"postman-echo.com/post?qwerty=One" \\
-H 'Cookie: sails.sid=s%3AGntztErGu9IDGjIBVu2-w7vTipGS3zsf.j9%2BHttqloZ2UJFwtSQbTx6tTTkOz2k6NkNq4NGCaDLI' \\
;`
}, function (err, result) {
expect(result.result).to.equal(false);
expect(result.reason).to.equal(
'Only the URL can be provided without an option preceding it. All other inputs must be specified via options.'
);
expect(result.result).to.equal(true);
expect(result.output.length).to.equal(1);
expect(result.output[0].type).to.equal('request');
expect(result.output[0].data.url).to.equal('postman-echo.com/post?qwerty=One');

const headerArr = result.output[0].data.header;
expect(headerArr[0].key).to.equal('Cookie');
expect(headerArr[0].value).to.equal('sails.sid=s%3AGntztErGu9IDGjIBVu2-w7vTipGS3zsf.j9%2BHttqloZ2UJFwtSQbTx6tTTkOz2k6NkNq4NGCaDLI');
done();
});
});
Expand Down Expand Up @@ -1069,4 +1102,220 @@ describe('Curl converter should', function() {
});
});
});

describe('It should correctly generate request for cURL with allowed bash operators', function() {

it('containing "<" or/and ">" in URL', function(done) {
convert({
type: 'string',
data: `curl https://httpbin.org/anything/<userId>/team \
-H 'authority: httpbin.org'
`
}, function (err, result) {
expect(result.result).to.equal(true);
expect(result.output.length).to.equal(1);
expect(result.output[0].type).to.equal('request');
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything/<userId>/team');
expect(result.output[0].data.method).to.equal('GET');
done();
});
});

it('containing "(" or/and ")" in URL', function(done) {
convert({
type: 'string',
data: `curl https://httpbin.org/anything/(userId)/team \
-H 'authority: httpbin.org'
`
}, function (err, result) {
expect(result.result).to.equal(true);
expect(result.output.length).to.equal(1);
expect(result.output[0].type).to.equal('request');
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything/(userId)/team');
expect(result.output[0].data.method).to.equal('GET');
done();
});
});

it('containing allowed character with url option defined in URL', function(done) {
convert({
type: 'string',
data: `curl --url https://httpbin.org/anything/<userId>/team \
-H 'authority: httpbin.org'
`
}, function (err, result) {
expect(result.result).to.equal(true);
expect(result.output.length).to.equal(1);
expect(result.output[0].type).to.equal('request');
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything/<userId>/team');
expect(result.output[0].data.method).to.equal('GET');
done();
});
});
});

describe('It should correctly generate request for cURL with non-allowed bash operators without error', function() {

it('containing non allowed operator "|" in URL', function(done) {
convert({
type: 'string',
data: `curl https://httpbin.org/anything/user|id/team \
-H 'authority: httpbin.org'
`
}, function (err, result) {
expect(result.result).to.equal(true);
expect(result.output.length).to.equal(1);
expect(result.output[0].type).to.equal('request');
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything/user');
expect(result.output[0].data.method).to.equal('GET');
done();
});
});

it('containing non allowed operator "&" in URL host part', function(done) {
convert({
type: 'string',
data: `curl https://httpbin.org/anything/user&id/team \
-H 'authority: httpbin.org'
`
}, function (err, result) {
expect(result.result).to.equal(true);
expect(result.output.length).to.equal(1);
expect(result.output[0].type).to.equal('request');
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything/user');
expect(result.output[0].data.method).to.equal('GET');
done();
});
});

it('containing "&" in URL query part', function(done) {
convert({
type: 'string',
data: `curl https://httpbin.org/anything?hello=world&how=areyou \
-H 'authority: httpbin.org'
`
}, function (err, result) {
expect(result.result).to.equal(true);
expect(result.output.length).to.equal(1);
expect(result.output[0].type).to.equal('request');
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything?hello=world&how=areyou');
expect(result.output[0].data.method).to.equal('GET');
done();
});
});

it('containing non allowed operator ";" in URL', function(done) {
convert({
type: 'string',
data: `curl https://httpbin.org/anything/user;id/team \
-H 'authority: httpbin.org'
`
}, function (err, result) {
expect(result.result).to.equal(true);
expect(result.output.length).to.equal(1);
expect(result.output[0].type).to.equal('request');
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything/user');
expect(result.output[0].data.method).to.equal('GET');
done();
});
});
});

it('It should correctly generate request for cURL with special characters in URL without error', function(done) {
convert({
type: 'string',
data: `curl https://httpbin.org/anything/!@$%^*-_=+.,\\{}[]/team \
-H 'authority: httpbin.org'
`
}, function (err, result) {
expect(result.result).to.equal(true);
expect(result.output.length).to.equal(1);
expect(result.output[0].type).to.equal('request');
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything/!@$%^*-_=+.,\{}[]/team');
expect(result.output[0].data.method).to.equal('GET');
done();
});
});

it('It should correctly generate request for cURL with extra arguments apart from URL without error', function(done) {
convert({
type: 'string',
data: `curl https://httpbin.org/anything/team 5678 \
-H 'authority: httpbin.org'
`
}, function (err, result) {
expect(result.result).to.equal(true);
expect(result.output.length).to.equal(1);
expect(result.output[0].type).to.equal('request');
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything/team');
expect(result.output[0].data.method).to.equal('GET');
done();
});
});

it('It should correctly generate request for cURL with allowed operators not in URL correctly', function(done) {
convert({
type: 'string',
data: `curl 'https://httpbin.org/anything' \
-H 'authority: httpbin.org' \
-H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) (KHTML, like Gecko) Chrome/109.0.0.0" \
-H 'accept: application/json, text/plain, */*' \
-H 'content-type: application/json' \
--data "{\"context\":{\"client\":{\"hl\":\"en\"}},\"state\":true}"
`
}, function (err, result) {
expect(result.result).to.equal(true);
expect(result.output.length).to.equal(1);
expect(result.output[0].type).to.equal('request');
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything');
expect(result.output[0].data.method).to.equal('POST');

const headerArr = result.output[0].data.header;
expect(headerArr.length).to.equal(4);
expect(headerArr[0].key).to.equal('authority');
expect(headerArr[0].value).to.equal('httpbin.org');
expect(headerArr[1].key).to.equal('user-agent');
expect(headerArr[1].value).to.equal('Mozilla/5.0 (Windows NT 10.0; Win64; x64) (KHTML, like Gecko) Chrome/109.0.0.0');
expect(headerArr[2].key).to.equal('accept');
expect(headerArr[2].value).to.equal('application/json, text/plain, */*');
expect(headerArr[3].key).to.equal('content-type');
expect(headerArr[3].value).to.equal('application/json');
done();
});
});

describe('It should correctly generate request for cURL with various postman id formats in URL', function() {

it('containing path variable with ":" character', function(done) {
convert({
type: 'string',
data: `curl https://httpbin.org/team/:teamId/user/:userId \
-H 'authority: httpbin.org'
`
}, function (err, result) {
expect(result.result).to.equal(true);
expect(result.output.length).to.equal(1);
expect(result.output[0].type).to.equal('request');
expect(result.output[0].data.url).to.equal('https://httpbin.org/team/:teamId/user/:userId');
expect(result.output[0].data.method).to.equal('GET');
done();
});
});

it('containing collection/environment variable in URL', function(done) {
convert({
type: 'string',
data: `curl {{baseUrl}}/user/{{userId}} \
-H 'authority: httpbin.org'
`
}, function (err, result) {
expect(result.result).to.equal(true);
expect(result.output.length).to.equal(1);
expect(result.output[0].type).to.equal('request');
expect(result.output[0].data.url).to.equal('{{baseUrl}}/user/{{userId}}');
expect(result.output[0].data.method).to.equal('GET');
done();
});
});
});
});

0 comments on commit 218d7d9

Please sign in to comment.