Skip to content

Commit a452e23

Browse files
authored
Merge pull request #12 from apoloa/add_curl_scripts
Support for cURL script generation
2 parents bce3003 + 8fef40d commit a452e23

File tree

6 files changed

+112
-6
lines changed

6 files changed

+112
-6
lines changed

cli.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ program
3333
.option('-o, --output <outputDir>', 'directory where to put the generated files (defaults to current directory)', parseOutput, process.cwd())
3434
.option('-t, --templates <templateDir>', 'directory where templates are located (defaults to internal nodejs templates)')
3535
.option('-b, --basedir <baseDir>', 'directory to use as the base when resolving local file references (defaults to OpenAPI file directory)')
36+
.option('-c, --curl', 'generate a curl scripts', false)
3637
.parse(process.argv);
3738

3839
if (!openapiFile) {
@@ -45,6 +46,7 @@ generator.generate({
4546
base_dir: program.basedir || baseDir || process.cwd(),
4647
target_dir: program.output,
4748
templates: program.templates ? path.resolve(process.cwd(), program.templates) : undefined,
49+
curl: program.curl,
4850
template,
4951
}).then(() => {
5052
console.log(green('Done! ✨'));

lib/beautifier.js

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ const OpenAPISampler = require('openapi-sampler');
22
const _ = require('lodash');
33
const slugg = require('slugg');
44
const md = require('markdown-it')();
5+
const curlGenerator = require('./curl-builder');
56

67
const sharedStart = (array) => {
78
const A = array.concat().sort();
8-
const a1 = A[0], a2= A[A.length-1], L= a1.length;
9+
const a1 = A[0], a2 = A[A.length - 1], L = a1.length;
910
let i = 0;
10-
while (i<L && a1.charAt(i)=== a2.charAt(i)) i++;
11+
while (i < L && a1.charAt(i) === a2.charAt(i)) i++;
1112
return a1.substring(0, i);
1213
};
1314

@@ -63,7 +64,7 @@ const beautifySchema = (schema) => {
6364
return schema;
6465
};
6566

66-
const beautifyOperation = (operation, operationName, pathName) => {
67+
const beautifyOperation = (operation, operationName, pathName, options) => {
6768
operation.slug = slugg(`op-${operationName}-${pathName}`);
6869
operation.summaryAsHTML = mdToHTML(operation.summary);
6970
operation.descriptionAsHTML = mdToHTML(operation.description);
@@ -89,6 +90,10 @@ const beautifyOperation = (operation, operationName, pathName) => {
8990
operation.queryParams = operation.parameters.filter(p => p.in === 'query');
9091
operation.pathParams = operation.parameters.filter(p => p.in === 'path');
9192
operation.cookieParams = operation.parameters.filter(p => p.in === 'cookie');
93+
94+
}
95+
if (options.curl) {
96+
operation.curl = curlGenerator(operation, operationName, pathName);
9297
}
9398

9499
if (operation.responses) {
@@ -118,7 +123,7 @@ const cleanBrackets = text => {
118123
return finalText;
119124
};
120125

121-
module.exports = (openapi) => {
126+
module.exports = (openapi, config) => {
122127
openapi.basePath = openapi.basePath || '';
123128
openapi.info = openapi.info || {};
124129
openapi.info.descriptionAsHTML = mdToHTML(openapi.info.description);
@@ -168,7 +173,7 @@ module.exports = (openapi) => {
168173
const httpMethods = ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'COPY', 'HEAD', 'OPTIONS', 'LINK', 'UNLIK', 'PURGE', 'LOCK', 'UNLOCK', 'PROPFIND'];
169174

170175
_.each(path, (operation, operationName) => {
171-
if (httpMethods.includes(operationName.toUpperCase())) beautifyOperation(operation, operationName, pathName);
176+
if (httpMethods.includes(operationName.toUpperCase())) beautifyOperation(operation, operationName, pathName, config);
172177
});
173178
});
174179

lib/curl-builder.js

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
const CONTENT_TYPE_HEADER = "Content-Type";
2+
3+
const curlBuilder = (operation, operationName, pathName) => {
4+
if (operation.requestBody && operation.requestBody.content) {
5+
return Object.keys(operation.requestBody.content).map((content) =>
6+
generateBodyBashPerContentType(operation, operationName, pathName, content,
7+
operation.requestBody.content[content].schema.generatedExample || operation.requestBody.content[content].schema.example)
8+
).join("\n");
9+
}
10+
return generateBodyCurl(operation, operationName, pathName);
11+
};
12+
13+
function generateBodyCurl(operation, operationName, pathName) {
14+
15+
let curlBuilder = new CurlBuilder(operationName, pathName, operation.pathParams);
16+
17+
if (operation.headers) {
18+
curlBuilder = curlBuilder.withHeaders(operation.headers)
19+
}
20+
21+
return curlBuilder.build();
22+
}
23+
24+
function generateBodyBashPerContentType(operation, operationName, pathName, contentType, requestBody) {
25+
26+
let curlBuilder = new CurlBuilder(operationName, pathName, operation.pathParams, operation.queryParams)
27+
.withContentTypeCurlHeader(contentType)
28+
.withContentTypeHeader(contentType)
29+
.withRequestBody(requestBody);
30+
if (operation.headers) {
31+
curlBuilder = curlBuilder.withHeaders(operation.headers)
32+
}
33+
34+
return curlBuilder.build();
35+
36+
}
37+
38+
class CurlBuilder {
39+
constructor(operationName, pathName, pathParams, queryParams) {
40+
let query = "";
41+
if (queryParams) {
42+
query = "?" + queryParams
43+
.map(queryParam => `${queryParam.name}=${queryParam.example || queryParam.name}`)
44+
.join("&");
45+
}
46+
if (pathParams) {
47+
pathParams.forEach(pathParams => {
48+
pathName = pathName.replace(`{${pathParams.name}}`, pathParams.example || pathParams.name)
49+
})
50+
}
51+
this.headers = [];
52+
this.body = "";
53+
this.curl = `curl -X "${operationName.toUpperCase()}" "${pathName}" \\`;
54+
}
55+
56+
withContentTypeCurlHeader(contentType) {
57+
this.curl = `# ${contentType} \n` + this.curl;
58+
return this;
59+
}
60+
61+
withContentTypeHeader(contentType) {
62+
this.headers.push(`-H '${CONTENT_TYPE_HEADER}: ${contentType}' \\`);
63+
return this;
64+
}
65+
66+
withHeaders(headers) {
67+
this.headers.push(...headers
68+
.map(header => `-H '${header.name}: ${header.example || header.name}' \\`));
69+
return this;
70+
}
71+
72+
withRequestBody(requestBody) {
73+
this.body += `-d $'{${JSON.stringify(requestBody)}} \\`;
74+
return this;
75+
}
76+
77+
build() {
78+
let curlScript = this.curl;
79+
if (this.headers.length !== 0) {
80+
curlScript += "\n" + this.headers.join("\n")
81+
}
82+
if (this.body.length !== 0) {
83+
curlScript += "\n" + this.body;
84+
}
85+
return curlScript.substr(0, curlScript.length - 1)
86+
}
87+
88+
}
89+
90+
module.exports = curlBuilder;

lib/generator.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ codegen.generate = config => new Promise((resolve, reject) => {
286286
bundle(config.openapi, config.base_dir)
287287
.catch(reject)
288288
.then((openapi) => {
289-
openapi = beautifier(openapi);
289+
openapi = beautifier(openapi, config);
290290

291291
const randomTitle = randomName().dashed;
292292
config.openapi = openapi;

templates/markdown/.partials/curl.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
##### cURL Script (generated)
2+
3+
```
4+
{{{curl}}}
5+
```

templates/markdown/.partials/operation.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@
88
{{{operation.description}}}
99
{{/if}}
1010

11+
{{#if operation.curl}}
12+
{{> curl curl=operation.curl}}
13+
{{/if}}
14+
1115
{{#if operation.pathParams}}
1216
{{> pathParams pathParams=operation.pathParams}}
1317
{{/if}}

0 commit comments

Comments
 (0)