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.
Files changed (67) hide show
  1. package/dist/index.html +1 -1
  2. package/dist/index.html.gz +0 -0
  3. package/dist/rapidoc-min.js +2 -5
  4. package/dist/rapidoc-min.js.LICENSE.txt +17 -1
  5. package/dist/rapidoc-min.js.LICENSE.txt.gz +0 -0
  6. package/dist/rapidoc-min.js.gz +0 -0
  7. package/dist/report.html +6 -5
  8. package/docker/Dockerfile +5 -5
  9. package/docs/api.html +48 -40
  10. package/docs/examples/auth-test1.html +113 -0
  11. package/docs/examples/auth.html +37 -34
  12. package/docs/examples/badges.html +1 -1
  13. package/docs/examples/dynamic-query-params.html +1 -0
  14. package/docs/examples/mongo.html +33 -0
  15. package/docs/examples/readwrite.html +40 -0
  16. package/docs/examples/slots.html +7 -0
  17. package/docs/examples.html +3 -2
  18. package/docs/index.html +2 -2
  19. package/docs/list.html +15 -3
  20. package/docs/quickstart.html +1 -1
  21. package/docs/rapidoc-min.js +2 -5
  22. package/docs/rapidoc-mini.html +20 -20
  23. package/docs/specs/auth-test1.yaml +35 -0
  24. package/docs/specs/auth.yaml +2 -2
  25. package/docs/specs/code-highlight.yaml +1 -1
  26. package/docs/specs/data-types.yaml +0 -1
  27. package/docs/specs/dynamic-query-params.yaml +41 -0
  28. package/docs/specs/examples.yaml +11 -0
  29. package/docs/specs/mongo.yaml +78 -0
  30. package/docs/specs/multipart-formdata.yaml +3 -0
  31. package/docs/specs/petstore_extended.yaml +1 -1
  32. package/docs/specs/readwrite.yaml +90 -0
  33. package/docs/specs/server.yaml +4 -4
  34. package/docs/specs/temp.json +24 -105
  35. package/docs/specs/temp.yaml +20 -282
  36. package/docs/welcome.html +3 -3
  37. package/index.html +11 -6
  38. package/package.json +22 -21
  39. package/src/components/api-request.js +105 -63
  40. package/src/components/api-response.js +10 -10
  41. package/src/components/json-tree.js +2 -1
  42. package/src/components/schema-table.js +51 -14
  43. package/src/components/schema-tree.js +59 -18
  44. package/src/components/tag-input.js +8 -1
  45. package/src/index.js +2 -1
  46. package/src/json-schema-viewer.js +341 -0
  47. package/src/rapidoc-mini.js +5 -1
  48. package/src/rapidoc.js +11 -7
  49. package/src/styles/font-styles.js +3 -2
  50. package/src/styles/nav-styles.js +2 -2
  51. package/src/templates/components-template.js +9 -10
  52. package/src/templates/endpoint-template.js +1 -1
  53. package/src/templates/expanded-endpoint-template.js +1 -1
  54. package/src/templates/focused-endpoint-template.js +1 -1
  55. package/src/templates/header-template.js +1 -1
  56. package/src/templates/json-schema-viewer-template.js +184 -0
  57. package/src/templates/main-body-template.js +4 -1
  58. package/src/templates/navbar-template.js +3 -3
  59. package/src/templates/overview-template.js +6 -3
  60. package/src/templates/security-scheme-template.js +78 -38
  61. package/src/templates/server-template.js +1 -1
  62. package/src/utils/common-utils.js +2 -1
  63. package/src/utils/schema-utils.js +24 -19
  64. package/src/utils/spec-parser.js +24 -3
  65. package/webpack.config.js +1 -12
  66. package/dist/rapidoc-min.js.map +0 -1
  67. 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
- export function applyApiKey(securitySchemeId, username = '', password = '', apikeyVal = '') {
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 apiKeyValue = '';
14
- if (securityObj.type && securityObj.scheme && securityObj.type === 'http' && securityObj.scheme.toLowerCase() === 'basic') {
16
+ let finalApiKeyValue = '';
17
+ if (securityObj.scheme?.toLowerCase() === 'basic') {
15
18
  if (username) {
16
- apiKeyValue = `Basic ${btoa(`${username}:${password}`)}`;
19
+ finalApiKeyValue = `Basic ${btoa(`${username}:${password}`)}`;
17
20
  }
18
- } else if (apikeyVal) {
19
- apiKeyValue = `${securityObj.scheme?.toLowerCase() === 'bearer' ? 'Bearer' : ''} ${apiKeyValue}`;
21
+ } else if (providedApikeyVal) {
22
+ securityObj.value = providedApikeyVal;
23
+ finalApiKeyValue = `${securityObj.scheme?.toLowerCase() === 'bearer' ? 'Bearer ' : ''}${providedApikeyVal}`;
20
24
  }
21
- if (apiKeyValue) {
22
- securityObj.finalKeyValue = apiKeyValue;
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', accessToken) {
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', securitySchemeId, authFlowDivEl, scopes = null, username = null, password = null) {
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.token_type, tokenResp.access_token);
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, sendClientSecretIn, securitySchemeId, authFlowDivEl);
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.token_type, msgEvent.data.access_token);
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, '', sendClientSecretIn, securitySchemeId, authFlowDivEl, selectedScopes);
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, '', sendClientSecretIn, securitySchemeId, authFlowDivEl, selectedScopes, username, password);
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=${() => { v.finalKeyValue = ''; this.requestUpdate(); }}>REMOVE</button>
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 (!pathScopes && Array.isArray(pSecurity[pathSecurityKey])) {
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) => html`
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
- ${orSecurityItem1.pathScopes !== ''
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> &nbsp;` : 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> &nbsp;` : 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>
@@ -1,5 +1,5 @@
1
1
  import { html } from 'lit-element';
2
- import marked from 'marked';
2
+ import { marked } from 'marked';
3
3
  import { unsafeHTML } from 'lit-html/directives/unsafe-html';
4
4
 
5
5
  export function setApiServer(serverUrl) {
@@ -10,10 +10,11 @@ export function debounce(fn, delay) {
10
10
  };
11
11
  }
12
12
 
13
- export const invalidCharsRegEx = new RegExp(/[\s#:?&={}]/, 'g'); // used for generating valid html element ids by replacing the invalid chars with hyphen (-)
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
- const finalExample = Object.assign(oneOfSamples[sampleKey], objWithSchemaProps);
393
- obj[`example-${i}`] = finalExample;
394
- addSchemaInfoToExample(schema.oneOf[key], obj[`example-${i}`]);
395
- i++;
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].deprecated && !config.includeDeprecated) { continue; }
440
- if (schema.properties[propertyName].readOnly && !config.includeReadOnly) { continue; }
441
- if (schema.properties[propertyName].writeOnly && !config.includeWriteOnly) { continue; }
442
- if (schema.properties[propertyName].type === 'array' || schema.properties[propertyName].items) {
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&lt;${schema.items.description}&gt;`
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 = '', schema, mimeType, includeReadOnly = true, includeWriteOnly = true, outputType) {
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.toLowerCase().includes('json')) {
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.toLowerCase().includes('json')) {
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.toLowerCase().includes('json') && typeof schema.example === 'object') ? 'json' : 'text'),
742
+ exampleFormat: ((mimeType?.toLowerCase().includes('json') && typeof schema.example === 'object') ? 'json' : 'text'),
738
743
  });
739
- } else if (mimeType.toLowerCase().includes('json') || mimeType.toLowerCase().includes('text') || mimeType.toLowerCase().includes('*/*') || mimeType.toLowerCase().includes('xml')) {
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.toLowerCase().includes('xml')) {
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.toLowerCase().includes('xml')) {
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
  }
@@ -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, sortEndpointsBy);
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, sortEndpointsBy) {
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'),