rapidoc 9.1.0 → 9.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.html +1 -1
- package/dist/index.html.gz +0 -0
- package/dist/rapidoc-min.js +2 -5
- package/dist/rapidoc-min.js.LICENSE.txt +17 -1
- package/dist/rapidoc-min.js.LICENSE.txt.gz +0 -0
- package/dist/rapidoc-min.js.gz +0 -0
- package/dist/report.html +6 -5
- package/docker/Dockerfile +5 -5
- package/docs/api.html +48 -40
- package/docs/examples/auth-test1.html +113 -0
- package/docs/examples/auth.html +37 -34
- package/docs/examples/badges.html +1 -1
- package/docs/examples/dynamic-query-params.html +1 -0
- package/docs/examples/mongo.html +33 -0
- package/docs/examples/readwrite.html +40 -0
- package/docs/examples/slots.html +7 -0
- package/docs/examples.html +3 -2
- package/docs/index.html +2 -2
- package/docs/list.html +15 -3
- package/docs/quickstart.html +1 -1
- package/docs/rapidoc-min.js +2 -5
- package/docs/rapidoc-mini.html +20 -20
- package/docs/specs/auth-test1.yaml +35 -0
- package/docs/specs/auth.yaml +2 -2
- package/docs/specs/code-highlight.yaml +1 -1
- package/docs/specs/data-types.yaml +0 -1
- package/docs/specs/dynamic-query-params.yaml +41 -0
- package/docs/specs/examples.yaml +11 -0
- package/docs/specs/mongo.yaml +78 -0
- package/docs/specs/multipart-formdata.yaml +3 -0
- package/docs/specs/petstore_extended.yaml +1 -1
- package/docs/specs/readwrite.yaml +90 -0
- package/docs/specs/server.yaml +4 -4
- package/docs/specs/temp.json +24 -105
- package/docs/specs/temp.yaml +20 -282
- package/docs/welcome.html +3 -3
- package/index.html +11 -6
- package/package.json +22 -21
- package/src/components/api-request.js +105 -63
- package/src/components/api-response.js +10 -10
- package/src/components/json-tree.js +2 -1
- package/src/components/schema-table.js +51 -14
- package/src/components/schema-tree.js +59 -18
- package/src/components/tag-input.js +8 -1
- package/src/index.js +2 -1
- package/src/json-schema-viewer.js +341 -0
- package/src/rapidoc-mini.js +5 -1
- package/src/rapidoc.js +11 -7
- package/src/styles/font-styles.js +3 -2
- package/src/styles/nav-styles.js +2 -2
- package/src/templates/components-template.js +9 -10
- package/src/templates/endpoint-template.js +1 -1
- package/src/templates/expanded-endpoint-template.js +1 -1
- package/src/templates/focused-endpoint-template.js +1 -1
- package/src/templates/header-template.js +1 -1
- package/src/templates/json-schema-viewer-template.js +184 -0
- package/src/templates/main-body-template.js +4 -1
- package/src/templates/navbar-template.js +3 -3
- package/src/templates/overview-template.js +6 -3
- package/src/templates/security-scheme-template.js +78 -38
- package/src/templates/server-template.js +1 -1
- package/src/utils/common-utils.js +2 -1
- package/src/utils/schema-utils.js +24 -19
- package/src/utils/spec-parser.js +24 -3
- package/webpack.config.js +1 -12
- package/dist/rapidoc-min.js.map +0 -1
- package/dist/rapidoc-min.js.map.gz +0 -0
|
@@ -1,25 +1,29 @@
|
|
|
1
|
+
/* eslint-disable arrow-body-style */
|
|
1
2
|
import { html } from 'lit-element';
|
|
2
3
|
import { unsafeHTML } from 'lit-html/directives/unsafe-html';
|
|
3
|
-
import marked from 'marked';
|
|
4
|
+
import { marked } from 'marked';
|
|
4
5
|
|
|
5
6
|
const codeVerifier = '731DB1C3F7EA533B85E29492D26AA-1234567890-1234567890';
|
|
6
7
|
const codeChallenge = '4FatVDBJKPAo4JgLLaaQFMUcQPn5CrPRvLlaob9PTYc'; // Base64 encoded SHA-256
|
|
7
8
|
|
|
8
|
-
|
|
9
|
+
const localStorageKey = 'rapidoc';
|
|
10
|
+
|
|
11
|
+
export function applyApiKey(securitySchemeId, username = '', password = '', providedApikeyVal = '') {
|
|
9
12
|
const securityObj = this.resolvedSpec.securitySchemes?.find((v) => (v.securitySchemeId === securitySchemeId));
|
|
10
13
|
if (!securityObj) {
|
|
11
14
|
return false;
|
|
12
15
|
}
|
|
13
|
-
let
|
|
14
|
-
if (securityObj.
|
|
16
|
+
let finalApiKeyValue = '';
|
|
17
|
+
if (securityObj.scheme?.toLowerCase() === 'basic') {
|
|
15
18
|
if (username) {
|
|
16
|
-
|
|
19
|
+
finalApiKeyValue = `Basic ${btoa(`${username}:${password}`)}`;
|
|
17
20
|
}
|
|
18
|
-
} else if (
|
|
19
|
-
|
|
21
|
+
} else if (providedApikeyVal) {
|
|
22
|
+
securityObj.value = providedApikeyVal;
|
|
23
|
+
finalApiKeyValue = `${securityObj.scheme?.toLowerCase() === 'bearer' ? 'Bearer ' : ''}${providedApikeyVal}`;
|
|
20
24
|
}
|
|
21
|
-
if (
|
|
22
|
-
securityObj.finalKeyValue =
|
|
25
|
+
if (finalApiKeyValue) {
|
|
26
|
+
securityObj.finalKeyValue = finalApiKeyValue;
|
|
23
27
|
this.requestUpdate();
|
|
24
28
|
return true;
|
|
25
29
|
}
|
|
@@ -36,6 +40,21 @@ export function onClearAllApiKeys() {
|
|
|
36
40
|
this.requestUpdate();
|
|
37
41
|
}
|
|
38
42
|
|
|
43
|
+
function getPersistedApiKeys() {
|
|
44
|
+
return JSON.parse(localStorage.getItem(localStorageKey)) || {};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function setPersistedApiKeys(obj) {
|
|
48
|
+
localStorage.setItem(localStorageKey, JSON.stringify(obj));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function recoverPersistedApiKeys() {
|
|
52
|
+
const rapidocLs = getPersistedApiKeys.call(this);
|
|
53
|
+
Object.values(rapidocLs).forEach((p) => {
|
|
54
|
+
applyApiKey.call(this, p.securitySchemeId, p.username, p.password, p.value);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
39
58
|
function onApiKeyChange(securitySchemeId) {
|
|
40
59
|
let apiKeyValue = '';
|
|
41
60
|
const securityObj = this.resolvedSpec.securitySchemes.find((v) => (v.securitySchemeId === securitySchemeId));
|
|
@@ -50,12 +69,17 @@ function onApiKeyChange(securitySchemeId) {
|
|
|
50
69
|
apiKeyValue = trEl.querySelector('.api-key-input').value.trim();
|
|
51
70
|
applyApiKey.call(this, securitySchemeId, '', '', apiKeyValue);
|
|
52
71
|
}
|
|
72
|
+
if (this.persistAuth === 'true') {
|
|
73
|
+
const rapidocLs = getPersistedApiKeys.call(this);
|
|
74
|
+
rapidocLs[securitySchemeId] = securityObj;
|
|
75
|
+
setPersistedApiKeys.call(this, rapidocLs);
|
|
76
|
+
}
|
|
53
77
|
}
|
|
54
78
|
}
|
|
55
79
|
}
|
|
56
80
|
|
|
57
81
|
// Updates the OAuth Access Token (API key), so it reflects in UI and gets used in TRY calls
|
|
58
|
-
function updateOAuthKey(securitySchemeId, tokenType = 'Bearer'
|
|
82
|
+
function updateOAuthKey(securitySchemeId, accessToken, tokenType = 'Bearer') {
|
|
59
83
|
const securityObj = this.resolvedSpec.securitySchemes.find((v) => (v.securitySchemeId === securitySchemeId));
|
|
60
84
|
securityObj.finalKeyValue = `${(tokenType.toLowerCase() === 'bearer' ? 'Bearer' : (tokenType.toLowerCase() === 'mac' ? 'MAC' : tokenType))} ${accessToken}`;
|
|
61
85
|
this.requestUpdate();
|
|
@@ -63,7 +87,7 @@ function updateOAuthKey(securitySchemeId, tokenType = 'Bearer', accessToken) {
|
|
|
63
87
|
|
|
64
88
|
/* eslint-disable no-console */
|
|
65
89
|
// Gets Access-Token in exchange of Authorization Code
|
|
66
|
-
async function fetchAccessToken(tokenUrl, clientId, clientSecret, redirectUrl, grantType, authCode, sendClientSecretIn = 'header',
|
|
90
|
+
async function fetchAccessToken(tokenUrl, clientId, clientSecret, redirectUrl, grantType, authCode, securitySchemeId, authFlowDivEl, sendClientSecretIn = 'header', scopes = null, username = null, password = null) {
|
|
67
91
|
const respDisplayEl = authFlowDivEl ? authFlowDivEl.querySelector('.oauth-resp-display') : undefined;
|
|
68
92
|
const urlFormParams = new URLSearchParams();
|
|
69
93
|
const headers = new Headers();
|
|
@@ -94,7 +118,7 @@ async function fetchAccessToken(tokenUrl, clientId, clientSecret, redirectUrl, g
|
|
|
94
118
|
const tokenResp = await resp.json();
|
|
95
119
|
if (resp.ok) {
|
|
96
120
|
if (tokenResp.token_type && tokenResp.access_token) {
|
|
97
|
-
updateOAuthKey.call(this, securitySchemeId, tokenResp.
|
|
121
|
+
updateOAuthKey.call(this, securitySchemeId, tokenResp.access_token, tokenResp.token_type);
|
|
98
122
|
if (respDisplayEl) {
|
|
99
123
|
respDisplayEl.innerHTML = '<span style="color:var(--green)">Access Token Received</span>';
|
|
100
124
|
}
|
|
@@ -130,10 +154,10 @@ async function onWindowMessageEvent(msgEvent, winObj, tokenUrl, clientId, client
|
|
|
130
154
|
if (msgEvent.data) {
|
|
131
155
|
if (msgEvent.data.responseType === 'code') {
|
|
132
156
|
// Authorization Code flow
|
|
133
|
-
fetchAccessToken.call(this, tokenUrl, clientId, clientSecret, redirectUrl, grantType, msgEvent.data.code,
|
|
157
|
+
fetchAccessToken.call(this, tokenUrl, clientId, clientSecret, redirectUrl, grantType, msgEvent.data.code, securitySchemeId, authFlowDivEl, sendClientSecretIn);
|
|
134
158
|
} else if (msgEvent.data.responseType === 'token') {
|
|
135
159
|
// Implicit flow
|
|
136
|
-
updateOAuthKey.call(this, securitySchemeId, msgEvent.data.
|
|
160
|
+
updateOAuthKey.call(this, securitySchemeId, msgEvent.data.access_token, msgEvent.data.token_type);
|
|
137
161
|
}
|
|
138
162
|
}
|
|
139
163
|
}
|
|
@@ -217,11 +241,11 @@ async function onInvokeOAuthFlow(securitySchemeId, flowType, authUrl, tokenUrl,
|
|
|
217
241
|
} else if (flowType === 'clientCredentials') {
|
|
218
242
|
grantType = 'client_credentials';
|
|
219
243
|
const selectedScopes = checkedScopeEls.map((v) => v.value).join(' ');
|
|
220
|
-
fetchAccessToken.call(this, tokenUrl, clientId, clientSecret, redirectUrlObj.toString(), grantType, '',
|
|
244
|
+
fetchAccessToken.call(this, tokenUrl, clientId, clientSecret, redirectUrlObj.toString(), grantType, '', securitySchemeId, authFlowDivEl, sendClientSecretIn, selectedScopes);
|
|
221
245
|
} else if (flowType === 'password') {
|
|
222
246
|
grantType = 'password';
|
|
223
247
|
const selectedScopes = checkedScopeEls.map((v) => v.value).join(' ');
|
|
224
|
-
fetchAccessToken.call(this, tokenUrl, clientId, clientSecret, redirectUrlObj.toString(), grantType, '',
|
|
248
|
+
fetchAccessToken.call(this, tokenUrl, clientId, clientSecret, redirectUrlObj.toString(), grantType, '', securitySchemeId, authFlowDivEl, sendClientSecretIn, selectedScopes, username, password);
|
|
225
249
|
}
|
|
226
250
|
}
|
|
227
251
|
/* eslint-enable no-console */
|
|
@@ -338,6 +362,20 @@ function oAuthFlowTemplate(flowName, clientId, clientSecret, securitySchemeId, a
|
|
|
338
362
|
`;
|
|
339
363
|
}
|
|
340
364
|
|
|
365
|
+
function removeApiKey(securitySchemeId) {
|
|
366
|
+
const securityObj = this.resolvedSpec.securitySchemes?.find((v) => (v.securitySchemeId === securitySchemeId));
|
|
367
|
+
securityObj.user = '';
|
|
368
|
+
securityObj.password = '';
|
|
369
|
+
securityObj.value = '';
|
|
370
|
+
securityObj.finalKeyValue = '';
|
|
371
|
+
if (this.persistAuth === 'true') {
|
|
372
|
+
const rapidocLs = getPersistedApiKeys.call(this);
|
|
373
|
+
delete rapidocLs[securityObj.securitySchemeId];
|
|
374
|
+
setPersistedApiKeys.call(this, rapidocLs);
|
|
375
|
+
}
|
|
376
|
+
this.requestUpdate();
|
|
377
|
+
}
|
|
378
|
+
|
|
341
379
|
export default function securitySchemeTemplate() {
|
|
342
380
|
if (!this.resolvedSpec) { return ''; }
|
|
343
381
|
const providedApiKeys = this.resolvedSpec.securitySchemes?.filter((v) => (v.finalKeyValue));
|
|
@@ -368,7 +406,7 @@ export default function securitySchemeTemplate() {
|
|
|
368
406
|
${v.finalKeyValue
|
|
369
407
|
? html`
|
|
370
408
|
<span class='blue-text'> ${v.finalKeyValue ? 'Key Applied' : ''} </span>
|
|
371
|
-
<button class="m-btn thin-border small" part="btn btn-outline" @click=${() => {
|
|
409
|
+
<button class="m-btn thin-border small" part="btn btn-outline" @click=${() => { removeApiKey.call(this, v.securitySchemeId); }}>REMOVE</button>
|
|
372
410
|
`
|
|
373
411
|
: ''
|
|
374
412
|
}
|
|
@@ -426,9 +464,7 @@ export default function securitySchemeTemplate() {
|
|
|
426
464
|
? html`
|
|
427
465
|
<tr>
|
|
428
466
|
<td style="border:none; padding-left:48px">
|
|
429
|
-
${Object.keys(v.flows).map((f) => oAuthFlowTemplate.call(
|
|
430
|
-
this, f, v['x-client-id'], v['x-client-secret'], v.securitySchemeId, v.flows[f],
|
|
431
|
-
))}
|
|
467
|
+
${Object.keys(v.flows).map((f) => oAuthFlowTemplate.call(this, f, v['x-client-id'], v['x-client-secret'], v.securitySchemeId, v.flows[f]))}
|
|
432
468
|
</td>
|
|
433
469
|
</tr>
|
|
434
470
|
`
|
|
@@ -449,19 +485,18 @@ export function pathSecurityTemplate(pathSecurity) {
|
|
|
449
485
|
pathSecurity.forEach((pSecurity) => {
|
|
450
486
|
const andSecurityKeys1 = [];
|
|
451
487
|
const andKeyTypes = [];
|
|
452
|
-
let pathScopes = '';
|
|
453
488
|
Object.keys(pSecurity).forEach((pathSecurityKey) => {
|
|
489
|
+
let pathScopes = '';
|
|
454
490
|
const s = this.resolvedSpec.securitySchemes.find((ss) => ss.securitySchemeId === pathSecurityKey);
|
|
455
|
-
if (
|
|
491
|
+
if (pSecurity[pathSecurityKey] && Array.isArray(pSecurity[pathSecurityKey])) {
|
|
456
492
|
pathScopes = pSecurity[pathSecurityKey].join(', ');
|
|
457
493
|
}
|
|
458
494
|
if (s) {
|
|
459
495
|
andKeyTypes.push(s.typeDisplay);
|
|
460
|
-
andSecurityKeys1.push(s);
|
|
496
|
+
andSecurityKeys1.push({ ...s, ...({ scopes: pathScopes }) });
|
|
461
497
|
}
|
|
462
498
|
});
|
|
463
499
|
orSecurityKeys1.push({
|
|
464
|
-
pathScopes,
|
|
465
500
|
securityTypes: andKeyTypes.length > 1 ? `${andKeyTypes[0]} + ${andKeyTypes.length - 1} more` : andKeyTypes[0],
|
|
466
501
|
securityDefs: andSecurityKeys1,
|
|
467
502
|
});
|
|
@@ -485,7 +520,20 @@ export function pathSecurityTemplate(pathSecurity) {
|
|
|
485
520
|
<div class="tooltip-text" style="position:absolute; color: var(--fg); top:26px; right:0; border:1px solid var(--border-color);padding:2px 4px; display:block;">
|
|
486
521
|
${orSecurityItem1.securityDefs.length > 1 ? html`<div>Requires <b>all</b> of the following </div>` : ''}
|
|
487
522
|
<div style="padding-left: 8px">
|
|
488
|
-
${orSecurityItem1.securityDefs.map((andSecurityItem, j) =>
|
|
523
|
+
${orSecurityItem1.securityDefs.map((andSecurityItem, j) => {
|
|
524
|
+
const scopeHtml = html`${andSecurityItem.scopes !== ''
|
|
525
|
+
? html`
|
|
526
|
+
<div>
|
|
527
|
+
<b>Required scopes:</b>
|
|
528
|
+
<br/>
|
|
529
|
+
<div style="margin-left:8px">
|
|
530
|
+
${andSecurityItem.scopes.split(',').map((scope, cnt) => html`${cnt === 0 ? '' : '┃'}<span>${scope}</span>`)}
|
|
531
|
+
</div>
|
|
532
|
+
</div>`
|
|
533
|
+
: ''
|
|
534
|
+
}`;
|
|
535
|
+
|
|
536
|
+
return html`
|
|
489
537
|
${andSecurityItem.type === 'oauth2'
|
|
490
538
|
? html`
|
|
491
539
|
<div>
|
|
@@ -494,31 +542,23 @@ export function pathSecurityTemplate(pathSecurity) {
|
|
|
494
542
|
: 'Needs'
|
|
495
543
|
}
|
|
496
544
|
OAuth Token <span style="font-family:var(--font-mono); color:var(--primary-color);">${andSecurityItem.securitySchemeId}</span> in <b>Authorization header</b>
|
|
497
|
-
${
|
|
498
|
-
? html`
|
|
499
|
-
<div>
|
|
500
|
-
<b>Required scopes:</b>
|
|
501
|
-
<br/>
|
|
502
|
-
<div style="margin-left:8px">
|
|
503
|
-
${orSecurityItem1.pathScopes.split(',').map((scope, cnt) => html`${cnt === 0 ? '' : '┃'}<span>${scope}</span>`)}
|
|
504
|
-
</div>
|
|
505
|
-
</div>`
|
|
506
|
-
: ''
|
|
507
|
-
}
|
|
545
|
+
${scopeHtml}
|
|
508
546
|
</div>`
|
|
509
547
|
: andSecurityItem.type === 'http'
|
|
510
548
|
? html`
|
|
511
549
|
<div>
|
|
512
550
|
${orSecurityItem1.securityDefs.length > 1 ? html`<b>${j + 1}.</b> ` : html`Requires`}
|
|
513
551
|
${andSecurityItem.scheme === 'basic' ? 'Base 64 encoded username:password' : 'Bearer Token'} in <b>Authorization header</b>
|
|
552
|
+
${scopeHtml}
|
|
514
553
|
</div>`
|
|
515
554
|
: html`
|
|
516
555
|
<div>
|
|
517
556
|
${orSecurityItem1.securityDefs.length > 1 ? html`<b>${j + 1}.</b> ` : html`Requires`}
|
|
518
557
|
Token in <b>${andSecurityItem.name} ${andSecurityItem.in}</b>
|
|
558
|
+
${scopeHtml}
|
|
519
559
|
</div>`
|
|
520
|
-
}
|
|
521
|
-
|
|
560
|
+
}`;
|
|
561
|
+
})}
|
|
522
562
|
</div>
|
|
523
563
|
</div>
|
|
524
564
|
</div>
|
|
@@ -10,10 +10,11 @@ export function debounce(fn, delay) {
|
|
|
10
10
|
};
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export const invalidCharsRegEx =
|
|
13
|
+
export const invalidCharsRegEx = /[\s#:?&={}]/g; // used for generating valid html element ids by replacing the invalid chars with hyphen (-)
|
|
14
14
|
export const rapidocApiKey = '_rapidoc_api_key';
|
|
15
15
|
|
|
16
16
|
export function sleep(ms) {
|
|
17
|
+
// eslint-disable-next-line no-promise-executor-return
|
|
17
18
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
18
19
|
}
|
|
19
20
|
|
|
@@ -389,10 +389,12 @@ export function schemaToSampleObj(schema, config = { }) {
|
|
|
389
389
|
const oneOfSamples = schemaToSampleObj(schema.oneOf[key], config);
|
|
390
390
|
for (const sampleKey in oneOfSamples) {
|
|
391
391
|
// 2. In the final example include a one-of item along with properties
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
392
|
+
if (oneOfSamples[sampleKey] === null) {
|
|
393
|
+
const finalExample = Object.assign(oneOfSamples[sampleKey], objWithSchemaProps);
|
|
394
|
+
obj[`example-${i}`] = finalExample;
|
|
395
|
+
addSchemaInfoToExample(schema.oneOf[key], obj[`example-${i}`]);
|
|
396
|
+
i++;
|
|
397
|
+
}
|
|
396
398
|
}
|
|
397
399
|
}
|
|
398
400
|
}
|
|
@@ -436,10 +438,10 @@ export function schemaToSampleObj(schema, config = { }) {
|
|
|
436
438
|
obj['example-0'] = schema.example;
|
|
437
439
|
} else {
|
|
438
440
|
for (const propertyName in schema.properties) {
|
|
439
|
-
if (schema.properties[propertyName]
|
|
440
|
-
if (schema.properties[propertyName]
|
|
441
|
-
if (schema.properties[propertyName]
|
|
442
|
-
if (schema.properties[propertyName]
|
|
441
|
+
if (schema.properties[propertyName]?.deprecated && !config.includeDeprecated) { continue; }
|
|
442
|
+
if (schema.properties[propertyName]?.readOnly && !config.includeReadOnly) { continue; }
|
|
443
|
+
if (schema.properties[propertyName]?.writeOnly && !config.includeWriteOnly) { continue; }
|
|
444
|
+
if (schema.properties[propertyName]?.type === 'array' || schema.properties[propertyName]?.items) {
|
|
443
445
|
if (schema.properties[propertyName].example) {
|
|
444
446
|
addPropertyExampleToObjectExamples(schema.properties[propertyName].example, obj, propertyName);
|
|
445
447
|
} else if (schema.properties[propertyName]?.items?.example) { // schemas and properties support single example but not multiple examples.
|
|
@@ -622,6 +624,7 @@ export function schemaInObjectNotation(schema, obj, level = 0, suffix = '') {
|
|
|
622
624
|
obj['::description'] = schema.description || '';
|
|
623
625
|
obj['::type'] = 'object';
|
|
624
626
|
obj['::deprecated'] = schema.deprecated || false;
|
|
627
|
+
obj['::readwrite'] = schema.readOnly ? 'readonly' : schema.writeOnly ? 'writeonly' : '';
|
|
625
628
|
for (const key in schema.properties) {
|
|
626
629
|
if (schema.required && schema.required.includes(key)) {
|
|
627
630
|
obj[`${key}*`] = schemaInObjectNotation(schema.properties[key], {}, (level + 1));
|
|
@@ -639,6 +642,8 @@ export function schemaInObjectNotation(schema, obj, level = 0, suffix = '') {
|
|
|
639
642
|
? `array<${schema.items.description}>`
|
|
640
643
|
: '';
|
|
641
644
|
obj['::type'] = 'array';
|
|
645
|
+
obj['::deprecated'] = schema.deprecated || false;
|
|
646
|
+
obj['::readwrite'] = schema.readOnly ? 'readonly' : schema.writeOnly ? 'writeonly' : '';
|
|
642
647
|
if (schema.items.items) {
|
|
643
648
|
obj['::array-type'] = schema.items.items.type;
|
|
644
649
|
}
|
|
@@ -654,14 +659,14 @@ export function schemaInObjectNotation(schema, obj, level = 0, suffix = '') {
|
|
|
654
659
|
}
|
|
655
660
|
|
|
656
661
|
/* Create Example object */
|
|
657
|
-
export function generateExample(examples = '', example = '',
|
|
662
|
+
export function generateExample(schema, mimeType, examples = '', example = '', includeReadOnly = true, includeWriteOnly = true, outputType = 'json', includeGeneratedExample = false) {
|
|
658
663
|
const finalExamples = [];
|
|
659
664
|
// First check if examples is provided
|
|
660
665
|
if (examples) {
|
|
661
666
|
for (const eg in examples) {
|
|
662
667
|
let egContent = '';
|
|
663
668
|
let egFormat = 'json';
|
|
664
|
-
if (mimeType
|
|
669
|
+
if (mimeType?.toLowerCase().includes('json')) {
|
|
665
670
|
if (outputType === 'text') {
|
|
666
671
|
egContent = typeof examples[eg].value === 'string' ? examples[eg].value : JSON.stringify(examples[eg].value, undefined, 2);
|
|
667
672
|
egFormat = 'text';
|
|
@@ -695,7 +700,7 @@ export function generateExample(examples = '', example = '', schema, mimeType, i
|
|
|
695
700
|
} else if (example) {
|
|
696
701
|
let egContent = '';
|
|
697
702
|
let egFormat = 'json';
|
|
698
|
-
if (mimeType
|
|
703
|
+
if (mimeType?.toLowerCase().includes('json')) {
|
|
699
704
|
if (outputType === 'text') {
|
|
700
705
|
egContent = typeof example === 'string' ? example : JSON.stringify(example, undefined, 2);
|
|
701
706
|
egFormat = 'text';
|
|
@@ -724,8 +729,8 @@ export function generateExample(examples = '', example = '', schema, mimeType, i
|
|
|
724
729
|
exampleFormat: egFormat,
|
|
725
730
|
});
|
|
726
731
|
}
|
|
727
|
-
// If schema-level examples are not provided then generate one based on the schema field types
|
|
728
|
-
if (finalExamples.length === 0) {
|
|
732
|
+
// If schema-level examples are not provided or includeGeneratedExample === true then generate one based on the schema field types
|
|
733
|
+
if (finalExamples.length === 0 || includeGeneratedExample === true) {
|
|
729
734
|
if (schema) {
|
|
730
735
|
if (schema.example) { // Note: schema.examples (plurals) is not allowed as per spec
|
|
731
736
|
finalExamples.push({
|
|
@@ -734,14 +739,14 @@ export function generateExample(examples = '', example = '', schema, mimeType, i
|
|
|
734
739
|
exampleDescription: '',
|
|
735
740
|
exampleType: mimeType,
|
|
736
741
|
exampleValue: schema.example,
|
|
737
|
-
exampleFormat: ((mimeType
|
|
742
|
+
exampleFormat: ((mimeType?.toLowerCase().includes('json') && typeof schema.example === 'object') ? 'json' : 'text'),
|
|
738
743
|
});
|
|
739
|
-
} else if (mimeType
|
|
744
|
+
} else if (mimeType?.toLowerCase().includes('json') || mimeType?.toLowerCase().includes('text') || mimeType?.toLowerCase().includes('*/*') || mimeType?.toLowerCase().includes('xml')) {
|
|
740
745
|
let xmlRootStart = '';
|
|
741
746
|
let xmlRootEnd = '';
|
|
742
747
|
let exampleFormat = '';
|
|
743
748
|
let exampleValue = '';
|
|
744
|
-
if (mimeType
|
|
749
|
+
if (mimeType?.toLowerCase().includes('xml')) {
|
|
745
750
|
xmlRootStart = (schema.xml && schema.xml.name) ? `<${schema.xml.name}>` : '<root>';
|
|
746
751
|
xmlRootEnd = (schema.xml && schema.xml.name) ? `</${schema.xml.name}>` : '</root>';
|
|
747
752
|
exampleFormat = 'text';
|
|
@@ -767,7 +772,7 @@ export function generateExample(examples = '', example = '', schema, mimeType, i
|
|
|
767
772
|
const description = samples[samplesKey]['::DESCRIPTION'] || '';
|
|
768
773
|
removeTitlesAndDescriptions(samples[samplesKey]);
|
|
769
774
|
|
|
770
|
-
if (mimeType
|
|
775
|
+
if (mimeType?.toLowerCase().includes('xml')) {
|
|
771
776
|
exampleValue = `${xmlRootStart}${json2xml(samples[samplesKey])}\n${xmlRootEnd}`;
|
|
772
777
|
} else {
|
|
773
778
|
exampleValue = outputType === 'text' ? JSON.stringify(samples[samplesKey], null, 2) : samples[samplesKey];
|
|
@@ -819,7 +824,7 @@ function getSerializeStyleForContentType(contentType) {
|
|
|
819
824
|
|
|
820
825
|
export function getSchemaFromParam(param) {
|
|
821
826
|
if (param.schema) {
|
|
822
|
-
return [param.schema, null];
|
|
827
|
+
return [param.schema, null, null];
|
|
823
828
|
}
|
|
824
829
|
if (param.content) {
|
|
825
830
|
// we gonna use the first content-encoding
|
|
@@ -829,5 +834,5 @@ export function getSchemaFromParam(param) {
|
|
|
829
834
|
}
|
|
830
835
|
}
|
|
831
836
|
}
|
|
832
|
-
return [null, null];
|
|
837
|
+
return [null, null, null];
|
|
833
838
|
}
|
package/src/utils/spec-parser.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* eslint-disable no-use-before-define */
|
|
2
2
|
import OpenApiParser from '@apitools/openapi-parser';
|
|
3
|
-
import marked from 'marked';
|
|
3
|
+
import { marked } from 'marked';
|
|
4
4
|
import { invalidCharsRegEx, rapidocApiKey, sleep } from '~/utils/common-utils';
|
|
5
5
|
|
|
6
6
|
export default async function ProcessSpec(specUrl, generateMissingTags = false, sortTags = false, sortEndpointsBy = '', attrApiKey = '', attrApiKeyLocation = '', attrApiKeyValue = '', serverUrl = '') {
|
|
@@ -14,6 +14,19 @@ export default async function ProcessSpec(specUrl, generateMissingTags = false,
|
|
|
14
14
|
specMeta = await OpenApiParser.resolve({ spec: specUrl }); // Swagger({ spec: specUrl });
|
|
15
15
|
}
|
|
16
16
|
await sleep(0); // important to show the initial loader (allows for rendering updates)
|
|
17
|
+
|
|
18
|
+
// If JSON Schema Viewer
|
|
19
|
+
if (specMeta.resolvedSpec?.jsonSchemaViewer && specMeta.resolvedSpec?.schemaAndExamples) {
|
|
20
|
+
this.dispatchEvent(new CustomEvent('before-render', { detail: { spec: specMeta.resolvedSpec } }));
|
|
21
|
+
const schemaAndExamples = Object.entries(specMeta.resolvedSpec.schemaAndExamples).map((v) => ({ show: true, expanded: true, selectedExample: null, name: v[0], elementId: v[0].replace(invalidCharsRegEx, '-'), ...v[1] }));
|
|
22
|
+
const parsedSpec = {
|
|
23
|
+
specLoadError: false,
|
|
24
|
+
isSpecLoading: false,
|
|
25
|
+
info: specMeta.resolvedSpec.info,
|
|
26
|
+
schemaAndExamples,
|
|
27
|
+
};
|
|
28
|
+
return parsedSpec;
|
|
29
|
+
}
|
|
17
30
|
if (specMeta.spec && (specMeta.spec.components || specMeta.spec.info || specMeta.spec.servers || specMeta.spec.tags || specMeta.spec.paths)) {
|
|
18
31
|
jsonParsedSpec = specMeta.spec;
|
|
19
32
|
this.dispatchEvent(new CustomEvent('before-render', { detail: { spec: jsonParsedSpec } }));
|
|
@@ -37,7 +50,7 @@ export default async function ProcessSpec(specUrl, generateMissingTags = false,
|
|
|
37
50
|
// const pathGroups = groupByPaths(jsonParsedSpec);
|
|
38
51
|
|
|
39
52
|
// Tags with Paths and WebHooks
|
|
40
|
-
const tags = groupByTags(jsonParsedSpec, generateMissingTags, sortTags
|
|
53
|
+
const tags = groupByTags(jsonParsedSpec, sortEndpointsBy, generateMissingTags, sortTags);
|
|
41
54
|
|
|
42
55
|
// Components
|
|
43
56
|
const components = getComponents(jsonParsedSpec);
|
|
@@ -226,7 +239,7 @@ function getComponents(openApiSpec) {
|
|
|
226
239
|
return components || [];
|
|
227
240
|
}
|
|
228
241
|
|
|
229
|
-
function groupByTags(openApiSpec, generateMissingTags = false, sortTags = false
|
|
242
|
+
function groupByTags(openApiSpec, sortEndpointsBy, generateMissingTags = false, sortTags = false) {
|
|
230
243
|
const supportedMethods = ['get', 'put', 'post', 'delete', 'patch', 'head', 'options']; // this is also used for ordering endpoints by methods
|
|
231
244
|
const tags = openApiSpec.tags && Array.isArray(openApiSpec.tags)
|
|
232
245
|
? openApiSpec.tags.map((v) => ({
|
|
@@ -318,6 +331,14 @@ function groupByTags(openApiSpec, generateMissingTags = false, sortTags = false,
|
|
|
318
331
|
finalParameters = pathOrHookObj.parameters ? pathOrHookObj.parameters.slice(0) : [];
|
|
319
332
|
}
|
|
320
333
|
|
|
334
|
+
// Filter callbacks to contain only objects.
|
|
335
|
+
if (pathOrHookObj.callbacks) {
|
|
336
|
+
for (const [callbackName, callbackConfig] of Object.entries(pathOrHookObj.callbacks)) {
|
|
337
|
+
const filteredCallbacks = Object.entries(callbackConfig).filter((entry) => typeof entry[1] === 'object') || [];
|
|
338
|
+
pathOrHookObj.callbacks[callbackName] = Object.fromEntries(filteredCallbacks);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
321
342
|
// Update Responses
|
|
322
343
|
tagObj.paths.push({
|
|
323
344
|
show: true,
|
package/webpack.config.js
CHANGED
|
@@ -49,18 +49,6 @@ const commonPlugins = [
|
|
|
49
49
|
},
|
|
50
50
|
},
|
|
51
51
|
}),
|
|
52
|
-
/*
|
|
53
|
-
new ESLintPlugin({
|
|
54
|
-
emitError: true,
|
|
55
|
-
emitWarning: true,
|
|
56
|
-
formatter: 'stylish',
|
|
57
|
-
overrideConfigFile: path.resolve(__dirname, '.eslintrc'),
|
|
58
|
-
outputReport: {
|
|
59
|
-
filePath: './eslint_report.html',
|
|
60
|
-
formatter: 'html',
|
|
61
|
-
},
|
|
62
|
-
}),
|
|
63
|
-
*/
|
|
64
52
|
];
|
|
65
53
|
|
|
66
54
|
if (process.env.NODE_ENV === 'production') {
|
|
@@ -156,6 +144,7 @@ module.exports = {
|
|
|
156
144
|
resolve: {
|
|
157
145
|
fallback: {
|
|
158
146
|
fs: false,
|
|
147
|
+
buffer: require.resolve('buffer'),
|
|
159
148
|
},
|
|
160
149
|
alias: {
|
|
161
150
|
'~': path.resolve(__dirname, 'src'),
|