Skip to content

Commit 218d7d9

Browse files
committed
Merge branch 'release/1.5.0'
2 parents e5a84c2 + 9e2232f commit 218d7d9

File tree

5 files changed

+303
-11
lines changed

5 files changed

+303
-11
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# cURL to Postman Importer Changelog
22

3+
#### v1.5.0 (March 31, 2023)
4+
* Fixed an issue where request generation failed for certain bash operators.
5+
* Fixed an issue where cURL with comments described was converted incorrectly.
6+
37
#### v1.4.0 (March 17, 2023)
48
* 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.
59

package-lock.json

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "curl-to-postmanv2",
3-
"version": "1.4.0",
3+
"version": "1.5.0",
44
"description": "Convert a given CURL command to a Postman request",
55
"main": "index.js",
66
"com_postman_plugin": {

src/lib.js

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ const commander = require('commander'),
33
shellQuote = require('../assets/shell-quote'),
44
unnecessaryOptions = require('../assets/unnecessaryOptions'),
55
supportedOptions = require('../assets/supportedOptions'),
6-
formDataOptions = ['-d', '--data', '--data-raw', '--data-binary', '--data-ascii'];
6+
formDataOptions = ['-d', '--data', '--data-raw', '--data-binary', '--data-ascii'],
7+
allowedOperators = ['<', '>', '(', ')'];
78

89
var program,
910

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

125-
// must have a URL
126-
if (curlObj.args.length > 1 && !curlObj.url) {
126+
/**
127+
* For cURL with ^ as line termination character, each such line termination char will be an separate arg.
128+
* throw an error as we have separate handling for parsing such cURLs
129+
* once it fails here using convertForCMDFormat()
130+
*/
131+
if (curlObj.args.length > 1 && _.includes(curlObj.args, '^')) {
127132
throw new Error('Only the URL can be provided without an option preceding it.' +
128133
' All other inputs must be specified via options.');
129134
}
@@ -444,8 +449,36 @@ var program,
444449
}
445450
/* eslint-enable */
446451
}
452+
else if (curlObj.args.length > 0) {
453+
let argStr = typeof curlObj.url === 'string' ? curlObj.url : '';
454+
455+
// eslint-disable-next-line consistent-return
456+
_.forEach(curlObj.args, (arg, index) => {
457+
const previousArgOp = _.get(curlObj.args, `${index - 1}.op`, ''),
458+
shouldAddCurrentArg = index === 0 || allowedOperators.includes(previousArgOp);
459+
460+
if (typeof arg === 'string' && shouldAddCurrentArg) {
461+
/**
462+
* Add current string arg only if previous arg is an allowed op.
463+
* For URL like "hello.com/<id>", as "<" and ">" are treated as bash operators,
464+
* we'll add such operator and next arg that was split up by it in URL
465+
*/
466+
argStr += arg;
467+
}
468+
else if (typeof arg === 'object' && allowedOperators.includes(arg.op)) {
469+
argStr += arg.op;
470+
}
471+
else {
472+
/**
473+
* Stop adding more args as soon as we know that args are not split by allowed operators.
474+
*/
475+
return false;
476+
}
477+
});
478+
this.requestUrl = argStr;
479+
}
447480
else {
448-
this.requestUrl = curlObj.args[0];
481+
throw new Error('Could not detect the URL from cURL. Please make sure it\'s a valid cURL');
449482
}
450483
},
451484

@@ -598,6 +631,12 @@ var program,
598631
curlObj = program.parse(sanitizedArgs);
599632
}
600633

634+
// Filter out comments from Args
635+
curlObj.args = _.filter(curlObj.args, (arg) => {
636+
// Each arg should be string itself, for comment we receive an object from parser
637+
return !(typeof arg === 'object' && typeof arg.comment === 'string');
638+
});
639+
601640
this.headerPairs = {};
602641

603642
// if method is not given in the curl command

test/conversion.test.js

Lines changed: 254 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,18 @@ describe('Curl converter should', function() {
4949
});
5050
});
5151

52+
it('throw an error for a cURL without URL defined correctly', function (done) {
53+
convert({
54+
type: 'string',
55+
data: 'curl -X POST -H \'Content-type: application/json\' #{reply_url} --data \'#{response.to_json}\''
56+
}, function (err, result) {
57+
expect(result.result).to.equal(false);
58+
expect(result.reason).to.equal('Error while parsing cURL: Could not identify the URL.' +
59+
' Please use the --url option.');
60+
done();
61+
});
62+
});
63+
5264
it('[Github #7390]: set request URL correctly irrespective of where it is mentioned', function (done) {
5365
convert({
5466
type: 'string',
@@ -168,6 +180,23 @@ describe('Curl converter should', function() {
168180
});
169181
});
170182

183+
it('convert a simple request with comment at the end correctly', function (done) {
184+
convert({
185+
type: 'string',
186+
data: 'curl --request GET --url http://www.google.com #comment1'
187+
}, function (err, result) {
188+
expect(result.result).to.equal(true);
189+
190+
expect(result.output.length).to.equal(1);
191+
expect(result.output[0].type).to.equal('request');
192+
193+
var request = result.output[0].data;
194+
expect(request.method).to.equal('GET');
195+
expect(request.url).to.equal('http://www.google.com');
196+
done();
197+
});
198+
});
199+
171200
it('convert a simple GET request', function (done) {
172201
var result = Converter.convertCurlToRequest('curl --request GET --url http://www.google.com');
173202
expect(result.method).to.equal('GET');
@@ -919,18 +948,22 @@ describe('Curl converter should', function() {
919948
});
920949
});
921950

922-
it('in case where there is a invalid character at the end it should throw an error', function(done) {
951+
it('in case where there is a invalid character at the end it should safely generate request', function(done) {
923952
convert({
924953
type: 'string',
925954
data: `curl --location --request POST \\
926955
"postman-echo.com/post?qwerty=One" \\
927956
-H 'Cookie: sails.sid=s%3AGntztErGu9IDGjIBVu2-w7vTipGS3zsf.j9%2BHttqloZ2UJFwtSQbTx6tTTkOz2k6NkNq4NGCaDLI' \\
928957
;`
929958
}, function (err, result) {
930-
expect(result.result).to.equal(false);
931-
expect(result.reason).to.equal(
932-
'Only the URL can be provided without an option preceding it. All other inputs must be specified via options.'
933-
);
959+
expect(result.result).to.equal(true);
960+
expect(result.output.length).to.equal(1);
961+
expect(result.output[0].type).to.equal('request');
962+
expect(result.output[0].data.url).to.equal('postman-echo.com/post?qwerty=One');
963+
964+
const headerArr = result.output[0].data.header;
965+
expect(headerArr[0].key).to.equal('Cookie');
966+
expect(headerArr[0].value).to.equal('sails.sid=s%3AGntztErGu9IDGjIBVu2-w7vTipGS3zsf.j9%2BHttqloZ2UJFwtSQbTx6tTTkOz2k6NkNq4NGCaDLI');
934967
done();
935968
});
936969
});
@@ -1069,4 +1102,220 @@ describe('Curl converter should', function() {
10691102
});
10701103
});
10711104
});
1105+
1106+
describe('It should correctly generate request for cURL with allowed bash operators', function() {
1107+
1108+
it('containing "<" or/and ">" in URL', function(done) {
1109+
convert({
1110+
type: 'string',
1111+
data: `curl https://httpbin.org/anything/<userId>/team \
1112+
-H 'authority: httpbin.org'
1113+
`
1114+
}, function (err, result) {
1115+
expect(result.result).to.equal(true);
1116+
expect(result.output.length).to.equal(1);
1117+
expect(result.output[0].type).to.equal('request');
1118+
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything/<userId>/team');
1119+
expect(result.output[0].data.method).to.equal('GET');
1120+
done();
1121+
});
1122+
});
1123+
1124+
it('containing "(" or/and ")" in URL', function(done) {
1125+
convert({
1126+
type: 'string',
1127+
data: `curl https://httpbin.org/anything/(userId)/team \
1128+
-H 'authority: httpbin.org'
1129+
`
1130+
}, function (err, result) {
1131+
expect(result.result).to.equal(true);
1132+
expect(result.output.length).to.equal(1);
1133+
expect(result.output[0].type).to.equal('request');
1134+
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything/(userId)/team');
1135+
expect(result.output[0].data.method).to.equal('GET');
1136+
done();
1137+
});
1138+
});
1139+
1140+
it('containing allowed character with url option defined in URL', function(done) {
1141+
convert({
1142+
type: 'string',
1143+
data: `curl --url https://httpbin.org/anything/<userId>/team \
1144+
-H 'authority: httpbin.org'
1145+
`
1146+
}, function (err, result) {
1147+
expect(result.result).to.equal(true);
1148+
expect(result.output.length).to.equal(1);
1149+
expect(result.output[0].type).to.equal('request');
1150+
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything/<userId>/team');
1151+
expect(result.output[0].data.method).to.equal('GET');
1152+
done();
1153+
});
1154+
});
1155+
});
1156+
1157+
describe('It should correctly generate request for cURL with non-allowed bash operators without error', function() {
1158+
1159+
it('containing non allowed operator "|" in URL', function(done) {
1160+
convert({
1161+
type: 'string',
1162+
data: `curl https://httpbin.org/anything/user|id/team \
1163+
-H 'authority: httpbin.org'
1164+
`
1165+
}, function (err, result) {
1166+
expect(result.result).to.equal(true);
1167+
expect(result.output.length).to.equal(1);
1168+
expect(result.output[0].type).to.equal('request');
1169+
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything/user');
1170+
expect(result.output[0].data.method).to.equal('GET');
1171+
done();
1172+
});
1173+
});
1174+
1175+
it('containing non allowed operator "&" in URL host part', function(done) {
1176+
convert({
1177+
type: 'string',
1178+
data: `curl https://httpbin.org/anything/user&id/team \
1179+
-H 'authority: httpbin.org'
1180+
`
1181+
}, function (err, result) {
1182+
expect(result.result).to.equal(true);
1183+
expect(result.output.length).to.equal(1);
1184+
expect(result.output[0].type).to.equal('request');
1185+
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything/user');
1186+
expect(result.output[0].data.method).to.equal('GET');
1187+
done();
1188+
});
1189+
});
1190+
1191+
it('containing "&" in URL query part', function(done) {
1192+
convert({
1193+
type: 'string',
1194+
data: `curl https://httpbin.org/anything?hello=world&how=areyou \
1195+
-H 'authority: httpbin.org'
1196+
`
1197+
}, function (err, result) {
1198+
expect(result.result).to.equal(true);
1199+
expect(result.output.length).to.equal(1);
1200+
expect(result.output[0].type).to.equal('request');
1201+
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything?hello=world&how=areyou');
1202+
expect(result.output[0].data.method).to.equal('GET');
1203+
done();
1204+
});
1205+
});
1206+
1207+
it('containing non allowed operator ";" in URL', function(done) {
1208+
convert({
1209+
type: 'string',
1210+
data: `curl https://httpbin.org/anything/user;id/team \
1211+
-H 'authority: httpbin.org'
1212+
`
1213+
}, function (err, result) {
1214+
expect(result.result).to.equal(true);
1215+
expect(result.output.length).to.equal(1);
1216+
expect(result.output[0].type).to.equal('request');
1217+
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything/user');
1218+
expect(result.output[0].data.method).to.equal('GET');
1219+
done();
1220+
});
1221+
});
1222+
});
1223+
1224+
it('It should correctly generate request for cURL with special characters in URL without error', function(done) {
1225+
convert({
1226+
type: 'string',
1227+
data: `curl https://httpbin.org/anything/!@$%^*-_=+.,\\{}[]/team \
1228+
-H 'authority: httpbin.org'
1229+
`
1230+
}, function (err, result) {
1231+
expect(result.result).to.equal(true);
1232+
expect(result.output.length).to.equal(1);
1233+
expect(result.output[0].type).to.equal('request');
1234+
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything/!@$%^*-_=+.,\{}[]/team');
1235+
expect(result.output[0].data.method).to.equal('GET');
1236+
done();
1237+
});
1238+
});
1239+
1240+
it('It should correctly generate request for cURL with extra arguments apart from URL without error', function(done) {
1241+
convert({
1242+
type: 'string',
1243+
data: `curl https://httpbin.org/anything/team 5678 \
1244+
-H 'authority: httpbin.org'
1245+
`
1246+
}, function (err, result) {
1247+
expect(result.result).to.equal(true);
1248+
expect(result.output.length).to.equal(1);
1249+
expect(result.output[0].type).to.equal('request');
1250+
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything/team');
1251+
expect(result.output[0].data.method).to.equal('GET');
1252+
done();
1253+
});
1254+
});
1255+
1256+
it('It should correctly generate request for cURL with allowed operators not in URL correctly', function(done) {
1257+
convert({
1258+
type: 'string',
1259+
data: `curl 'https://httpbin.org/anything' \
1260+
-H 'authority: httpbin.org' \
1261+
-H "user-agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) (KHTML, like Gecko) Chrome/109.0.0.0" \
1262+
-H 'accept: application/json, text/plain, */*' \
1263+
-H 'content-type: application/json' \
1264+
--data "{\"context\":{\"client\":{\"hl\":\"en\"}},\"state\":true}"
1265+
`
1266+
}, function (err, result) {
1267+
expect(result.result).to.equal(true);
1268+
expect(result.output.length).to.equal(1);
1269+
expect(result.output[0].type).to.equal('request');
1270+
expect(result.output[0].data.url).to.equal('https://httpbin.org/anything');
1271+
expect(result.output[0].data.method).to.equal('POST');
1272+
1273+
const headerArr = result.output[0].data.header;
1274+
expect(headerArr.length).to.equal(4);
1275+
expect(headerArr[0].key).to.equal('authority');
1276+
expect(headerArr[0].value).to.equal('httpbin.org');
1277+
expect(headerArr[1].key).to.equal('user-agent');
1278+
expect(headerArr[1].value).to.equal('Mozilla/5.0 (Windows NT 10.0; Win64; x64) (KHTML, like Gecko) Chrome/109.0.0.0');
1279+
expect(headerArr[2].key).to.equal('accept');
1280+
expect(headerArr[2].value).to.equal('application/json, text/plain, */*');
1281+
expect(headerArr[3].key).to.equal('content-type');
1282+
expect(headerArr[3].value).to.equal('application/json');
1283+
done();
1284+
});
1285+
});
1286+
1287+
describe('It should correctly generate request for cURL with various postman id formats in URL', function() {
1288+
1289+
it('containing path variable with ":" character', function(done) {
1290+
convert({
1291+
type: 'string',
1292+
data: `curl https://httpbin.org/team/:teamId/user/:userId \
1293+
-H 'authority: httpbin.org'
1294+
`
1295+
}, function (err, result) {
1296+
expect(result.result).to.equal(true);
1297+
expect(result.output.length).to.equal(1);
1298+
expect(result.output[0].type).to.equal('request');
1299+
expect(result.output[0].data.url).to.equal('https://httpbin.org/team/:teamId/user/:userId');
1300+
expect(result.output[0].data.method).to.equal('GET');
1301+
done();
1302+
});
1303+
});
1304+
1305+
it('containing collection/environment variable in URL', function(done) {
1306+
convert({
1307+
type: 'string',
1308+
data: `curl {{baseUrl}}/user/{{userId}} \
1309+
-H 'authority: httpbin.org'
1310+
`
1311+
}, function (err, result) {
1312+
expect(result.result).to.equal(true);
1313+
expect(result.output.length).to.equal(1);
1314+
expect(result.output[0].type).to.equal('request');
1315+
expect(result.output[0].data.url).to.equal('{{baseUrl}}/user/{{userId}}');
1316+
expect(result.output[0].data.method).to.equal('GET');
1317+
done();
1318+
});
1319+
});
1320+
});
10721321
});

0 commit comments

Comments
 (0)