Skip to content

Commit 5c59f6c

Browse files
committed
Added logic to include OpenAPI examples to PDF generation (with documentation & examples).
1 parent a90a542 commit 5c59f6c

5 files changed

Lines changed: 266 additions & 5 deletions

File tree

docs/api.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,12 @@ <h2 style="margin: 0px 0 4px 0;"> Attributes</h2>
187187
<td class="gray">true to include list of all the APIs and their summary at the end in the generated PDF </td>
188188
<td>false</td>
189189
</tr>
190+
191+
<tr>
192+
<td class="mono-bold">include-example</td>
193+
<td class="gray">true to include OpenAPI-specified examples in the generated PDF </td>
194+
<td>false</td>
195+
</tr>
190196
</table>
191197
</div>
192198

docs/examples/example2.html

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<!doctype html>
2+
<html lang="en">
3+
<head>
4+
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
5+
<title>Example RapiPdf Output with API Examples</title>
6+
<script type="text/javascript" src="../rapipdf-min.js"></script>
7+
</head>
8+
<body>
9+
<rapi-pdf
10+
spec-url="../specs/openapi3_with_examples.json"
11+
style = "font-size: 10px"
12+
pdf-schema-style="table"
13+
include-example="true" />
14+
</body>
15+
</html>
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
{
2+
"openapi": "3.0.1",
3+
"info": {
4+
"title": "API including examples",
5+
"description": "The following describes a not real API.",
6+
"contact": {
7+
"name": "No One",
8+
"url": "http://www.centauricorp.com"
9+
},
10+
"version": "0.0.0"
11+
},
12+
"servers": [
13+
{
14+
"url": "http://notreal.org/",
15+
"description": "Not a real server",
16+
"variables": {}
17+
},
18+
{
19+
"url": "http://alsonotreal.org/",
20+
"description": "Also not a real server",
21+
"variables": {}
22+
}
23+
],
24+
"paths": {
25+
"/rest/first": {
26+
"get": {
27+
"tags": [
28+
"First"
29+
],
30+
"summary": "The first REST-ful path.",
31+
"operationId": "getFirst",
32+
"parameters": [
33+
{
34+
"name": "paramOne",
35+
"in": "query",
36+
"description": "First parameter.",
37+
"required": true,
38+
"schema": {
39+
"type": "string"
40+
}
41+
},
42+
{
43+
"name": "paramTwo",
44+
"in": "query",
45+
"description": "Second parameter.",
46+
"schema": {
47+
"type": "string"
48+
}
49+
},
50+
{
51+
"name": "paramThree",
52+
"in": "query",
53+
"description": "Third parameter.",
54+
"schema": {
55+
"type": "string"
56+
}
57+
},
58+
{
59+
"name": "paramFour",
60+
"in": "query",
61+
"description": "Fourth parameter.",
62+
"required": true,
63+
"schema": {
64+
"type": "string"
65+
}
66+
},
67+
{
68+
"name": "paramFive",
69+
"in": "query",
70+
"description": "Fifth parameter.",
71+
"required": true,
72+
"schema": {
73+
"type": "string"
74+
}
75+
}
76+
],
77+
"responses": {
78+
"200": {
79+
"description": "Successful response.",
80+
"content": {
81+
"application/json": {
82+
"example": {
83+
"propertyOne": "Value15880",
84+
"propertyTwo": "Value23718",
85+
"propertyThree": [
86+
{
87+
"subpropertyOne": "Value3",
88+
"subpropertyTwo": "Value4",
89+
"subpropertyThree": "Value5",
90+
"subpropertyFour": {
91+
"subsubpropertyOne": "Value6",
92+
"subsubpropertyTwo": "Value7"
93+
},
94+
"subproperyFive": null
95+
}
96+
]
97+
}
98+
}
99+
}
100+
},
101+
"5XX": {
102+
"description": "Internal Server Error"
103+
}
104+
}
105+
}
106+
},
107+
"/rest/second": {
108+
"put": {
109+
"tags": [
110+
"Second"
111+
],
112+
"summary": "The second REST-ful path.",
113+
"operationId": "putSecond",
114+
"requestBody": {
115+
"description": "The second REST-ful path's request body.",
116+
"content": {
117+
"application/json": {
118+
"schema": {
119+
"type": "object"
120+
},
121+
"example": {
122+
"paramOne": "valueOne",
123+
"paramTwo": "valueTwo",
124+
"paramThree": [
125+
{
126+
"subparamOne": "valueThree",
127+
"subparamTwo": "valueFour",
128+
"subparamThree": "valueFive",
129+
"subparamFour": "valueSix"
130+
},
131+
{
132+
"subparamOne": "valueSeven",
133+
"subparamTwo": "valueEight",
134+
"subparamThree": "valueNine",
135+
"subparamFour": "valueTen"
136+
}
137+
],
138+
"paramFour": [
139+
{
140+
"subparamFive": [
141+
{
142+
"subsubparamOne": "valueTen",
143+
"subsubparamTwo": "valueEleven",
144+
"subsubparamThree": "valueTwelve",
145+
"subsubparamFour": "valueThirteen"
146+
}
147+
],
148+
"subparamSix": [
149+
{
150+
"subsubparamFive": "valueFourteen",
151+
"subsubparamSix": "valueFifteen",
152+
"subsubparamSeven": "valueSixteen",
153+
"subsubparamEight": "valueSeventeen"
154+
},
155+
{
156+
"subsubparamFive": "valueEighteen",
157+
"subsubparamSix": "valueNineteen",
158+
"subsubparamSeven": "valueTwenty",
159+
"subsubparamEight": "valueTwentyOne"
160+
}
161+
],
162+
"subparamSeven": {}
163+
}
164+
]
165+
}
166+
}
167+
},
168+
"required": true
169+
},
170+
"responses": {
171+
"200": {
172+
"description": "Custom List Inserted/Updated",
173+
"content": {
174+
"text/plain": {
175+
"schema": {
176+
"type": "string"
177+
},
178+
"examples": {
179+
"First example": { "value": "More success" },
180+
"Second example": { "value": "Even More success" },
181+
"Third example": { "value": "All success" },
182+
"Fourth example": { "value": "Less success" }
183+
}
184+
}
185+
}
186+
},
187+
"5XX": {
188+
"description": "Internal Server Error",
189+
"content": {
190+
"text/plain": {
191+
"schema": {
192+
"type": "string"
193+
},
194+
"example": "Not so much"
195+
}
196+
}
197+
}
198+
}
199+
}
200+
}
201+
},
202+
"components": {
203+
"securitySchemes": {
204+
"basic": {
205+
"type": "http",
206+
"description": "authentication needed to complete action",
207+
"scheme": "basic"
208+
}
209+
}
210+
}
211+
}

src/pdf-gen.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export default async function createPdf(specUrl, options) {
3939
red: { color: 'orangered' },
4040
blue: { color: '#005b96' },
4141
mono: { font: 'RobotoMono', fontSize: 10 },
42+
monoSub: { font: 'RobotoMono', fontSize: 8 },
4243
};
4344

4445
const allContent = [];

src/pdf-parts-gen.js

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -181,8 +181,28 @@ function getParameterTableDef(parameters, paramType, localize, includeExample =
181181
];
182182
}
183183

184+
function getExamplesDef(contentTypeObj, localizedExampleLabel) {
185+
const exampleSectionDef = [];
186+
if (contentTypeObj.example) {
187+
exampleSectionDef.push([
188+
{ text: `${localizedExampleLabel}:`, margin: [20, 10, 0, 0], style: ['small', 'b'] },
189+
{ text: JSON.stringify(contentTypeObj.example, null, 2), margin: [40, 10, 0, 0], style: 'monoSub' },
190+
]);
191+
}
192+
if (contentTypeObj.examples) {
193+
let iterCount = 0;
194+
for (const oneExample in contentTypeObj.examples) {
195+
exampleSectionDef.push([
196+
{ text: `${localizedExampleLabel} ${++iterCount}:`, margin: [20, 10, 0, 0], style: ['small', 'b'] },
197+
{ text: JSON.stringify(oneExample, null, 2), margin: [40, 10, 0, 0], style: 'monoSub' },
198+
]);
199+
}
200+
}
201+
return exampleSectionDef;
202+
}
203+
184204
// Request Body Def
185-
function getRequestBodyDef(requestBody, schemaStyle, localize) {
205+
function getRequestBodyDef(requestBody, schemaStyle, localize, includeExample = false) {
186206
if (!requestBody) {
187207
return;
188208
}
@@ -212,7 +232,7 @@ function getRequestBodyDef(requestBody, schemaStyle, localize) {
212232
}
213233
requestBodyDef.push(treeDef);
214234
} else {
215-
// if Schema Style is Tree
235+
// Schema style is "tree."
216236
let schemaTableTreeDef;
217237
if (schemaInObjectNotaion['::type'] && schemaInObjectNotaion['::type'] === 'array') {
218238
schemaTableTreeDef = objectToTableTree(schemaInObjectNotaion['::prop'], localize, 'array');
@@ -239,12 +259,16 @@ function getRequestBodyDef(requestBody, schemaStyle, localize) {
239259
}
240260
content.push(requestBodyDef);
241261
}
262+
263+
if (includeExample) {
264+
content.push(getExamplesDef(contentTypeObj, localize.example));
265+
}
242266
}
243267
return content;
244268
}
245269

246270
// Response Def
247-
function getResponseDef(responses, schemaStyle, localize) {
271+
function getResponseDef(responses, schemaStyle, localize, includeExample = false) {
248272
const respDef = [];
249273
for (const statusCode in responses) {
250274
const allResponseDefs = [];
@@ -253,7 +277,8 @@ function getResponseDef(responses, schemaStyle, localize) {
253277
{ text: `${localize.responseModel} - ${contentType}`, margin: [10, 10, 0, 0], style: ['small', 'b'] },
254278
];
255279

256-
let origSchema = responses[statusCode].content[contentType].schema;
280+
const contentTypeObj = responses[statusCode].content[contentType];
281+
let origSchema = contentTypeObj.schema;
257282
if (origSchema) {
258283
origSchema = JSON.parse(JSON.stringify(origSchema));
259284
const schemaInObjectNotaion = schemaInObjectNotation(origSchema);
@@ -299,6 +324,9 @@ function getResponseDef(responses, schemaStyle, localize) {
299324
}
300325
}
301326
}
327+
if (includeExample) {
328+
responseDef.push(getExamplesDef(contentTypeObj, localize.example));
329+
}
302330
allResponseDefs.push(responseDef);
303331
}
304332
respDef.push({
@@ -399,7 +427,7 @@ export function getApiDef(spec, filterPath, schemaStyle, localize, includeExampl
399427

400428
// Generate Response Defs
401429
operationContent.push({ text: localize.response, style: ['p', 'b', 'alternate'], margin: [0, 10, 0, 0] });
402-
const respDef = getResponseDef(path.responses, schemaStyle, localize);
430+
const respDef = getResponseDef(path.responses, schemaStyle, localize, includeExample);
403431
if (respDef && respDef.length > 0) {
404432
operationContent.push({
405433
stack: respDef,

0 commit comments

Comments
 (0)