-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcurl2vRO.js
More file actions
321 lines (282 loc) · 12.3 KB
/
curl2vRO.js
File metadata and controls
321 lines (282 loc) · 12.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
/**
* @module Curl2vRO
* @requires fs
* @requires curl-to-postmanv2
* @requires deasync
* @fileoverview Curl to vRO JavaScript Code Converter
* @description A utility to convert curl commands to VMware vRealize Orchestrator (vRO) JavaScript code
* @author Mayank Goyal <mayankgoyalmax@gmail.com>
* @version 2.0.2
* @license MIT
*/
const { validate, convert } = require('curl-to-postmanv2');
const fs = require('fs');
const deasync = require('deasync');
// Create a synchronous version of the convert function
function convertSync(curlCommand) {
let done = false;
let result = null;
let error = null;
convert({ type: 'string', data: curlCommand }, (err, res) => {
if (err) {
error = err;
} else {
result = res;
}
done = true;
});
// Wait synchronously for the async operation to complete
deasync.loopWhile(() => !done);
if (error) throw error;
return result;
}
/**
* Converts a Postman request object to vRO JavaScript code
* @param {Object} postmanRequest - The Postman request object
* @param {string} originalCurl - The original curl command for reference
* @returns {string} The generated vRO JavaScript code
*/
function generateVROCode(postmanRequest, originalCurl) {
if (!postmanRequest || !postmanRequest.url) {
throw new Error('Invalid Postman request object');
}
const method = postmanRequest.method || 'GET';
const url = typeof postmanRequest.url === 'string'
? postmanRequest.url
: (postmanRequest.url.raw || '');
if (!url) {
throw new Error('No URL found in the request');
}
// Check for FTP protocol
if (url.toLowerCase().startsWith('ftp://')) {
throw new Error('FTP protocol is not supported. Please use HTTP or HTTPS.');
}
// Parse URL to extract components
let urlObj;
let urlToProcess = url;
// Add https:// if no protocol (http:// or https://) is specified
if (!/^https?:\/\//i.test(urlToProcess)) {
urlToProcess = 'https://' + urlToProcess;
}
try {
urlObj = new URL(urlToProcess);
} catch (e) {
throw new Error(`Invalid URL format: ${url}`);
}
// Get current date for JSDoc
const currentDate = new Date().toISOString().split('T')[0];
// Start building the vRO code
let vroCode = `/**
* @description Makes a ${method} REST call to ${url}
* @author curl2vRO (Mayank Goyal)
*/
// Define URL components
var targetUrl = "${urlObj.protocol}//${urlObj.host}${urlObj.pathname}${urlObj.search || ''}";
var baseUrl = "${urlObj.protocol}//${urlObj.host}";
// Accept SSL certificate
var ld = Config.getKeystores().getImportCAFromUrlAction();
var model = ld.getModel();
model.value = baseUrl;
var error = ld.execute();
if (error) {
throw new Error("Failed to accept certificate for URL: " + baseUrl + ". Error: " + error);
}
// Create transient REST host
var restHost = RESTHostManager.createHost("dynamicRequest");
var httpRestHost = RESTHostManager.createTransientHostFrom(restHost);
httpRestHost.operationTimeout = 600;
httpRestHost.url = baseUrl;
// Create REST request
var request = httpRestHost.createRequest("${method.toUpperCase()}", "${urlObj.pathname}${urlObj.search || ''}");
`;
// Add headers
const headers = postmanRequest.header || [];
if (headers.length > 0) {
vroCode += '\n// Add headers\n';
headers.forEach(h => {
if (h.key && h.value) {
// Skip Content-Length as it's handled automatically
if (h.key.toLowerCase() !== 'content-length') {
const escapedValue = h.value.replace(/"/g, '\\"');
vroCode += `request.setHeader("${h.key}", "${escapedValue}");\n`;
}
}
});
}
// Helper function to check if string is JSON
function isJsonString(str) {
try {
JSON.parse(str);
return true;
} catch (e) {
return false;
}
}
// Add request body if present
if (postmanRequest.body) {
if (postmanRequest.body.mode === 'raw' && postmanRequest.body.raw) {
// Get content type from headers
const contentTypeHeader = headers.find(h => h.key.toLowerCase() === 'content-type');
const contentType = contentTypeHeader ? contentTypeHeader.value.toLowerCase() : '';
const body = postmanRequest.body.raw;
// Handle different content types
if (contentType.includes('application/json') || isJsonString(body)) {
// Handle JSON content
const jsonBody = JSON.parse(body);
vroCode += '\n// Set JSON request body\nrequest.contentType = "application/json";\n';
vroCode += `request.content = JSON.stringify(${JSON.stringify(jsonBody, null, 2)});\n`;
} else if (contentType.includes('application/xml') || contentType.includes('text/xml') ||
(contentType === '' && body.trim().startsWith('<'))) {
// Handle XML content
const xmlContent = body.replace(/`/g, '\\`'); // Escape backticks in XML
vroCode += `\n// Set XML request body\nrequest.contentType = "${contentType || 'application/xml'}";\n`;
vroCode += `request.content = \`${xmlContent}\`;\n`;
} else if (contentType === '*/*' || !contentType) {
// Handle wildcard or no content type
if (body.trim().startsWith('<')) {
// Looks like XML
const xmlContent = body.replace(/`/g, '\\`');
vroCode += '\n// Set request body (inferred XML)\nrequest.contentType = "application/xml";\n';
vroCode += `request.content = \`${xmlContent}\`;\n`;
} else if (isJsonString(body)) {
// Try parsing as JSON
const jsonBody = JSON.parse(body);
vroCode += '\n// Set request body (inferred JSON)\nrequest.contentType = "application/json";\n';
vroCode += `request.content = JSON.stringify(${JSON.stringify(jsonBody, null, 2)});\n`;
} else {
// Default to text/plain
const textContent = body.replace(/`/g, '\\`');
vroCode += '\n// Set request body (default text)\nrequest.contentType = "text/plain";\n';
vroCode += `request.content = \`${textContent}\`;\n`;
}
} else {
// Handle other content types as-is
const escapedContent = body.replace(/`/g, '\\`');
vroCode += `\n// Set ${contentType} request body\nrequest.contentType = "${contentType}";\n`;
vroCode += `request.content = \`${escapedContent}\`;\n`;
}
} else if (postmanRequest.body.mode === 'formdata' && postmanRequest.body.formdata) {
// Handle form data
vroCode += '\n// Set form data\n';
postmanRequest.body.formdata.forEach(item => {
if (item.key) {
const value = item.value || '';
vroCode += `request.addParameter("${item.key}", "${value}");\n`;
}
});
} else if (postmanRequest.body.mode === 'urlencoded' && postmanRequest.body.urlencoded) {
// Handle URL encoded form data
vroCode += '\n// Set URL encoded form data\n';
postmanRequest.body.urlencoded.forEach(item => {
if (item.key) {
const value = item.value || '';
vroCode += `request.addParameter("${item.key}", "${value}");\n`;
}
});
}
}
// Add execution code
vroCode += `
// Execute REST request
var response = request.execute();
// Handle response
if (response.statusCode >= 200 && response.statusCode < 300) {
System.log("Request successful");
try {
// Try to parse JSON response
var responseContent = JSON.parse(response.contentAsString);
System.log(JSON.stringify(responseContent, null, 2));
return responseContent;
} catch (e) {
// If not JSON, return as text
System.log(response.contentAsString);
return response.contentAsString;
}
} else {
var errorMessage = "Request failed with status: " + response.statusCode;
try {
// Try to include error details from response
var errorContent = JSON.parse(response.contentAsString);
errorMessage += " - " + (errorContent.message || errorContent.error || JSON.stringify(errorContent));
} catch (e) {
errorMessage += " - " + response.contentAsString;
}
throw errorMessage;
}
`;
// Add the original curl command as a comment
vroCode += `\n/*\nOriginal curl command:\n${originalCurl}\n*/`;
return vroCode;
}
/**
* Converts a curl command to vRO JavaScript code
* @param {string} curlCommand - The curl command to convert
* @param {Object} [options] - Conversion options
* @param {boolean} [options.writeToFile=false] - Whether to write the output to a file. Defaults to false for safety in read-only environments.
* @returns {string} The generated vRO JavaScript code
* @throws {Error} If the curl command cannot be parsed properly
*/
function convertCurlToVRO(curlCommand, { writeToFile = false } = {}) {
try {
// First validate the curl command
const validation = validate(curlCommand);
if (!validation.result) {
throw new Error(`Invalid curl command: ${validation.reason || 'Unknown error'}`);
}
// Convert the curl command to Postman format synchronously
const result = convertSync(curlCommand);
if (!result || !result.result || !result.output || !result.output[0]) {
throw new Error('Failed to convert curl command: Invalid response from converter');
}
const postmanRequest = result.output[0].data;
// Generate vRO code from the Postman request
const vroCode = generateVROCode(postmanRequest, curlCommand);
// Generate a filename from the URL and method
const urlParts = postmanRequest.url;
let host, path;
if (typeof urlParts === 'string') {
// For string URLs, ensure we have a protocol before creating URL object
const urlStr = urlParts.startsWith('http') ? urlParts : 'https://' + urlParts;
const urlObj = new URL(urlStr);
host = urlObj.hostname;
path = urlObj.pathname.replace(/[^a-zA-Z0-9]/g, '_');
} else {
host = urlParts.host ? urlParts.host.join('.') : 'request';
path = urlParts.path ? urlParts.path.join('_') : '';
}
const filename = `${host}_${path}_${postmanRequest.method || 'get'}.js`
.replace(/_{2,}/g, '_')
.replace(/^_|_$/g, '');
// Write to file if enabled
if (writeToFile) {
try {
fs.writeFileSync(filename, vroCode);
console.log(`vRO code has been saved to ${filename}`);
} catch (error) {
console.warn(`Warning: Could not write to file '${filename}': ${error.message}. The code will still be returned.`);
}
}
return vroCode;
} catch (error) {
const errorMessage = `Failed to process curl command.\n` +
`This might be due to an unsupported curl command format. Please try the following steps:\n` +
`1. First convert your curl command using Postman's import feature:\n` +
` https://learning.postman.com/docs/getting-started/importing-and-exporting/importing-curl-commands/\n` +
`2. Copy the simplified curl command from Postman and try again.\n\n` +
`Common issues with the current command:\n` +
`- Missing 'https://' or 'http://' prefix in URL\n` +
`- Complex quoting or escaping in the command\n` +
`- Unsupported curl options or parameters\n\n` +
`Original error: ${error.message}`;
throw new Error(errorMessage);
}
}
// For backward compatibility
const convertCurlToVROAsync = (curlCommand, options) => {
return convertCurlToVRO(curlCommand, { ...options, writeToFile: false });
};
// Export the module
module.exports = {
convertCurlToVRO,
convertCurlToVROAsync
};