rapidoc 9.1.3 → 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 (52) hide show
  1. package/dist/index.html +1 -1
  2. package/dist/index.html.gz +0 -0
  3. package/dist/rapidoc-min.js +2 -2
  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 +12 -5
  10. package/docs/examples/dynamic-query-params.html +1 -0
  11. package/docs/examples/readwrite.html +40 -0
  12. package/docs/examples.html +3 -2
  13. package/docs/index.html +2 -2
  14. package/docs/list.html +6 -2
  15. package/docs/quickstart.html +1 -1
  16. package/docs/rapidoc-min.js +2 -2
  17. package/docs/specs/code-highlight.yaml +1 -1
  18. package/docs/specs/data-types.yaml +0 -1
  19. package/docs/specs/examples.yaml +11 -0
  20. package/docs/specs/petstore_extended.yaml +1 -1
  21. package/docs/specs/readwrite.yaml +90 -0
  22. package/docs/specs/temp.json +24 -105
  23. package/docs/specs/temp.yaml +20 -282
  24. package/docs/welcome.html +3 -3
  25. package/index.html +8 -5
  26. package/package.json +22 -21
  27. package/src/components/api-request.js +78 -41
  28. package/src/components/api-response.js +10 -10
  29. package/src/components/schema-table.js +51 -14
  30. package/src/components/schema-tree.js +59 -18
  31. package/src/components/tag-input.js +8 -1
  32. package/src/json-schema-viewer.js +1 -1
  33. package/src/rapidoc-mini.js +5 -1
  34. package/src/rapidoc.js +5 -1
  35. package/src/styles/font-styles.js +3 -2
  36. package/src/styles/nav-styles.js +2 -2
  37. package/src/templates/components-template.js +5 -5
  38. package/src/templates/endpoint-template.js +1 -1
  39. package/src/templates/expanded-endpoint-template.js +1 -1
  40. package/src/templates/focused-endpoint-template.js +1 -1
  41. package/src/templates/header-template.js +1 -1
  42. package/src/templates/json-schema-viewer-template.js +8 -8
  43. package/src/templates/main-body-template.js +4 -1
  44. package/src/templates/navbar-template.js +2 -2
  45. package/src/templates/overview-template.js +5 -2
  46. package/src/templates/security-scheme-template.js +48 -13
  47. package/src/templates/server-template.js +1 -1
  48. package/src/utils/common-utils.js +2 -1
  49. package/src/utils/schema-utils.js +14 -9
  50. package/src/utils/spec-parser.js +3 -3
  51. package/webpack.config.js +1 -0
  52. package/docs/specs/tenants.json +0 -12
@@ -1,5 +1,5 @@
1
1
  import { LitElement, html, css } 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
  import FontStyles from '~/styles/font-styles';
5
5
  import SchemaStyles from '~/styles/schema-styles';
@@ -115,7 +115,6 @@ export default class SchemaTree extends LitElement {
115
115
  this.data['::type'] === 'array' ? this.data['::props'] : this.data,
116
116
  this.data['::type'],
117
117
  this.data['::array-type'] || '',
118
-
119
118
  )}`
120
119
  : html`<span class='mono-font' style='color:var(--red)'> Schema not found </span>`
121
120
  }
@@ -123,7 +122,28 @@ export default class SchemaTree extends LitElement {
123
122
  `;
124
123
  }
125
124
 
126
- generateTree(data, dataType = 'object', arrayType = '', key = '', description = '', schemaLevel = 0, indentLevel = 0) {
125
+ generateTree(data, dataType = 'object', arrayType = '', key = '', description = '', schemaLevel = 0, indentLevel = 0, readOrWrite = '') {
126
+ if (this.schemaHideReadOnly === 'true') {
127
+ if (dataType === 'array') {
128
+ if (readOrWrite === 'readonly') {
129
+ return;
130
+ }
131
+ }
132
+ if (data['::readwrite'] === 'readonly') {
133
+ return;
134
+ }
135
+ }
136
+ if (this.schemaHideWriteOnly === 'true') {
137
+ if (dataType === 'array') {
138
+ if (readOrWrite === 'writeonly') {
139
+ return;
140
+ }
141
+ }
142
+ if (data['::readwrite'] === 'writeonly') {
143
+ return;
144
+ }
145
+ }
146
+
127
147
  if (!data) {
128
148
  return html`<div class="null" style="display:inline;">null</div>`;
129
149
  }
@@ -189,13 +209,13 @@ export default class SchemaTree extends LitElement {
189
209
  <div class="td key ${data['::deprecated'] ? 'deprecated' : ''}" style='min-width:${minFieldColWidth}px'>
190
210
  ${data['::type'] === 'xxx-of-option' || data['::type'] === 'xxx-of-array' || key.startsWith('::OPTION')
191
211
  ? html`<span class='key-label xxx-of-key'>${keyLabel}</span><span class="xxx-of-descr">${keyDescr}</span>`
192
- : keyLabel.endsWith('*')
193
- ? html`<span class="key-label">${keyLabel.substring(0, keyLabel.length - 1)}</span><span style='color:var(--red);'>*</span>`
194
- : keyLabel === '::props' || keyLabel === '::ARRAY~OF'
195
- ? ''
196
- : schemaLevel > 0
197
- ? html`<span class="key-label">${keyLabel}:</span>`
198
- : ''
212
+ : keyLabel === '::props' || keyLabel === '::ARRAY~OF'
213
+ ? ''
214
+ : schemaLevel > 0
215
+ ? html`<span class="key-label" title="${readOrWrite === 'readonly' ? 'Read-Only' : readOrWrite === 'writeonly' ? 'Write-Only' : ''}">
216
+ ${keyLabel.replace(/\*$/, '')}${keyLabel.endsWith('*') ? html`<span style="color:var(--red)">*</span>` : ''}${readOrWrite === 'readonly' ? html` 🆁` : readOrWrite === 'writeonly' ? html` 🆆` : readOrWrite}:
217
+ </span>`
218
+ : ''
199
219
  }
200
220
  ${data['::type'] === 'xxx-of' && dataType === 'array' ? html`<span style="color:var(--primary-color)">ARRAY</span>` : ''}
201
221
  ${openBracket}
@@ -204,10 +224,10 @@ export default class SchemaTree extends LitElement {
204
224
  </div>
205
225
  <div class='inside-bracket ${data['::type'] || 'no-type-info'}' style='padding-left:${data['::type'] === 'xxx-of-option' || data['::type'] === 'xxx-of-array' ? 0 : leftPadding}px;'>
206
226
  ${Array.isArray(data) && data[0]
207
- ? html`${this.generateTree(data[0], 'xxx-of-option', '', '::ARRAY~OF', '', newSchemaLevel, newIndentLevel)}`
227
+ ? html`${this.generateTree(data[0], 'xxx-of-option', '', '::ARRAY~OF', '', newSchemaLevel, newIndentLevel, data[0]['::readwrite'])}`
208
228
  : html`
209
229
  ${Object.keys(data).map((dataKey) => html`
210
- ${['::description', '::type', '::props', '::deprecated', '::array-type'].includes(dataKey)
230
+ ${['::description', '::type', '::props', '::deprecated', '::array-type', '::readwrite'].includes(dataKey)
211
231
  ? data[dataKey]['::type'] === 'array' || data[dataKey]['::type'] === 'object'
212
232
  ? html`${this.generateTree(
213
233
  data[dataKey]['::type'] === 'array' ? data[dataKey]['::props'] : data[dataKey],
@@ -217,6 +237,7 @@ export default class SchemaTree extends LitElement {
217
237
  data[dataKey]['::description'],
218
238
  newSchemaLevel,
219
239
  newIndentLevel,
240
+ data[dataKey]['::readwrite'] ? data[dataKey]['::readwrite'] : '',
220
241
  )}`
221
242
  : ''
222
243
  : html`${this.generateTree(
@@ -227,6 +248,7 @@ export default class SchemaTree extends LitElement {
227
248
  data[dataKey]['::description'],
228
249
  newSchemaLevel,
229
250
  newIndentLevel,
251
+ data[dataKey]['::readwrite'] ? data[dataKey]['::readwrite'] : '',
230
252
  )}`
231
253
  }
232
254
  `)}
@@ -240,15 +262,34 @@ export default class SchemaTree extends LitElement {
240
262
  `;
241
263
  }
242
264
 
243
- // For Primitive Data types
244
- const [type, readorWriteOnly, constraint, defaultValue, allowedValues, pattern, schemaDescription, , deprecated] = data.split('~|~');
245
- if (readorWriteOnly === '🆁' && this.schemaHideReadOnly === 'true') {
265
+ // For Primitive types and array of Primitives
266
+ const [type, primitiveReadOrWrite, constraint, defaultValue, allowedValues, pattern, schemaDescription, , deprecated] = data.split('~|~');
267
+ if (primitiveReadOrWrite === '🆁' && this.schemaHideReadOnly === 'true') {
246
268
  return;
247
269
  }
248
- if (readorWriteOnly === '🆆' && this.schemaHideWriteOnly === 'true') {
270
+ if (primitiveReadOrWrite === '🆆' && this.schemaHideWriteOnly === 'true') {
249
271
  return;
250
272
  }
251
273
  const dataTypeCss = type.replace(/┃.*/g, '').replace(/[^a-zA-Z0-9+]/g, '').substring(0, 4).toLowerCase();
274
+
275
+ let finalReadWriteText = '';
276
+ let finalReadWriteTip = '';
277
+ if (dataType === 'array') {
278
+ if (readOrWrite === 'readonly') {
279
+ finalReadWriteText = '🆁';
280
+ finalReadWriteTip = 'Read-Only';
281
+ } else if (readOrWrite === 'writeonly') {
282
+ finalReadWriteText = '🆆';
283
+ finalReadWriteTip = 'Write-Only';
284
+ }
285
+ } else if (primitiveReadOrWrite === '🆁') {
286
+ finalReadWriteText = '🆁';
287
+ finalReadWriteTip = 'Read-Only';
288
+ } else if (primitiveReadOrWrite === '🆆') {
289
+ finalReadWriteText = '🆆';
290
+ finalReadWriteTip = 'Write-Only';
291
+ }
292
+
252
293
  return html`
253
294
  <div class = "tr primitive">
254
295
  <div class="td key ${deprecated}" style='min-width:${minFieldColWidth}px' >
@@ -258,9 +299,9 @@ export default class SchemaTree extends LitElement {
258
299
  ? html`<span class='key-label xxx-of-key'>${keyLabel}</span><span class="xxx-of-descr">${keyDescr}</span>`
259
300
  : html`<span class="key-label">${keyLabel}:</span>`
260
301
  }
261
- <span class="${dataTypeCss}" >
302
+ <span class="${dataTypeCss}" title="${finalReadWriteTip}">
262
303
  ${dataType === 'array' ? `[${type}]` : `${type}`}
263
- ${readorWriteOnly}
304
+ ${finalReadWriteText}
264
305
  </span>
265
306
  </div>
266
307
  <div class='td key-descr'>
@@ -38,7 +38,14 @@ export default class TagInput extends LitElement {
38
38
  afterPaste(e) {
39
39
  const clipboardData = e.clipboardData || window.clipboardData;
40
40
  const pastedData = clipboardData.getData('Text');
41
- this.value = pastedData ? pastedData.split(',').filter((v) => v.trim() !== '') : '';
41
+ const pastedArray = pastedData ? pastedData.split(',').filter((v) => v.trim() !== '') : '';
42
+ if (pastedArray) {
43
+ if (Array.isArray(this.value)) {
44
+ this.value = [...this.value, ...pastedArray];
45
+ } else {
46
+ this.value = pastedArray;
47
+ }
48
+ }
42
49
  e.preventDefault();
43
50
  }
44
51
 
@@ -1,5 +1,5 @@
1
1
  import { css, LitElement } from 'lit-element';
2
- import marked from 'marked';
2
+ import { marked } from 'marked';
3
3
  import Prism from 'prismjs';
4
4
  import 'prismjs/components/prism-css';
5
5
  import 'prismjs/components/prism-yaml';
@@ -1,5 +1,5 @@
1
1
  import { css, LitElement } from 'lit-element';
2
- import marked from 'marked';
2
+ import { marked } from 'marked';
3
3
  import Prism from 'prismjs';
4
4
  import 'prismjs/components/prism-css';
5
5
  import 'prismjs/components/prism-yaml';
@@ -49,6 +49,7 @@ export default class RapiDocMini extends LitElement {
49
49
  responseAreaHeight: { type: String, attribute: 'response-area-height' },
50
50
  showSummaryWhenCollapsed: { type: String, attribute: 'show-summary-when-collapsed' },
51
51
  fillRequestFieldsWithExample: { type: String, attribute: 'fill-request-fields-with-example' },
52
+ persistAuth: { type: String, attribute: 'persist-auth' },
52
53
 
53
54
  // Schema Styles
54
55
  schemaStyle: { type: String, attribute: 'schema-style' },
@@ -166,6 +167,7 @@ export default class RapiDocMini extends LitElement {
166
167
  if (!this.schemaExpandLevel || this.schemaExpandLevel < 1) { this.schemaExpandLevel = 99999; }
167
168
  if (!this.schemaDescriptionExpanded || !'true, false,'.includes(`${this.schemaDescriptionExpanded},`)) { this.schemaDescriptionExpanded = 'false'; }
168
169
  if (!this.fillRequestFieldsWithExample || !'true, false,'.includes(`${this.fillRequestFieldsWithExample},`)) { this.fillRequestFieldsWithExample = 'true'; }
170
+ if (!this.persistAuth || !'true, false,'.includes(`${this.persistAuth},`)) { this.persistAuth = 'false'; }
169
171
  if (!this.responseAreaHeight) { this.responseAreaHeight = '300px'; }
170
172
 
171
173
  if (!this.allowTry || !'true, false,'.includes(`${this.allowTry},`)) { this.allowTry = 'true'; }
@@ -318,6 +320,8 @@ export default class RapiDocMini extends LitElement {
318
320
  }
319
321
  }
320
322
  this.requestUpdate();
323
+ // eslint-disable-next-line no-await-in-loop
324
+ while (!await this.updateComplete);
321
325
  const specLoadedEvent = new CustomEvent('spec-loaded', { detail: spec });
322
326
  this.dispatchEvent(specLoadedEvent);
323
327
  }
package/src/rapidoc.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { css, LitElement, unsafeCSS } from 'lit-element';
2
- import marked from 'marked';
2
+ import { marked } from 'marked';
3
3
  import Prism from 'prismjs';
4
4
  import 'prismjs/components/prism-css';
5
5
  import 'prismjs/components/prism-yaml';
@@ -63,6 +63,7 @@ export default class RapiDoc extends LitElement {
63
63
  defaultSchemaTab: { type: String, attribute: 'default-schema-tab' },
64
64
  responseAreaHeight: { type: String, attribute: 'response-area-height' },
65
65
  fillRequestFieldsWithExample: { type: String, attribute: 'fill-request-fields-with-example' },
66
+ persistAuth: { type: String, attribute: 'persist-auth' },
66
67
  onNavTagClick: { type: String, attribute: 'on-nav-tag-click' },
67
68
 
68
69
  // Schema Styles
@@ -434,6 +435,7 @@ export default class RapiDoc extends LitElement {
434
435
  this.schemaHideReadOnly += ['get', 'head', 'delete', 'options'];
435
436
  this.schemaHideWriteOnly = this.schemaHideWriteOnly !== 'never';
436
437
  if (!this.fillRequestFieldsWithExample || !'true, false,'.includes(`${this.fillRequestFieldsWithExample},`)) { this.fillRequestFieldsWithExample = 'true'; }
438
+ if (!this.persistAuth || !'true, false,'.includes(`${this.persistAuth},`)) { this.persistAuth = 'false'; }
437
439
  if (!this.onNavTagClick || !'expand-collapse, show-description,'.includes(`${this.onNavTagClick},`)) { this.onNavTagClick = 'expand-collapse'; }
438
440
  if (!this.responseAreaHeight) {
439
441
  this.responseAreaHeight = '300px';
@@ -709,6 +711,8 @@ export default class RapiDoc extends LitElement {
709
711
  }
710
712
  }
711
713
  this.requestUpdate();
714
+ // eslint-disable-next-line no-await-in-loop
715
+ while (!await this.updateComplete);
712
716
  const specLoadedEvent = new CustomEvent('spec-loaded', { detail: spec });
713
717
  this.dispatchEvent(specLoadedEvent);
714
718
 
@@ -88,8 +88,9 @@ export default css`
88
88
  font-size: var(--font-size-regular);
89
89
  line-height:calc(var(--font-size-regular) + 8px);
90
90
  }
91
- .m-markdown li{
92
- line-height:calc(var(--font-size-regular) + 12px);
91
+ .m-markdown li {
92
+ font-size: var(--font-size-regular);
93
+ line-height:calc(var(--font-size-regular) + 10px);
93
94
  }
94
95
 
95
96
  .m-markdown-small p,
@@ -15,8 +15,8 @@ export default css`
15
15
  flex-wrap:nowrap;
16
16
  word-break:break-word;
17
17
  }
18
- ::slotted(*){
19
- padding:16px 30px 0 16px;
18
+ ::slotted([slot=nav-logo]){
19
+ padding:16px 16px 0 16px;
20
20
  }
21
21
  .nav-scroll {
22
22
  overflow-x: hidden;
@@ -1,6 +1,6 @@
1
1
  import { html } from 'lit-element';
2
2
  import { unsafeHTML } from 'lit-html/directives/unsafe-html';
3
- import marked from 'marked';
3
+ import { marked } from 'marked';
4
4
  import { schemaInObjectNotation } from '~/utils/schema-utils';
5
5
  import '~/components/json-tree';
6
6
  import '~/components/schema-tree';
@@ -19,8 +19,8 @@ function schemaBodyTemplate(sComponent) {
19
19
  schema-expand-level = "${this.schemaExpandLevel}"
20
20
  schema-description-expanded = "${this.schemaDescriptionExpanded}"
21
21
  allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}",
22
- schema-hide-read-only = false
23
- schema-hide-write-only = ${this.schemaHideWriteOnly}
22
+ schema-hide-read-only = "false"
23
+ schema-hide-write-only = "${this.schemaHideWriteOnly}"
24
24
  > </schema-tree> `
25
25
  : html`
26
26
  <schema-tree
@@ -29,8 +29,8 @@ function schemaBodyTemplate(sComponent) {
29
29
  schema-expand-level = "${this.schemaExpandLevel}"
30
30
  schema-description-expanded = "${this.schemaDescriptionExpanded}"
31
31
  allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}",
32
- schema-hide-read-only = false
33
- schema-hide-write-only = ${this.schemaHideWriteOnly}
32
+ schema-hide-read-only = "false"
33
+ schema-hide-write-only = "${this.schemaHideWriteOnly}"
34
34
  > </schema-tree>`
35
35
  }
36
36
  </div>`;
@@ -1,6 +1,6 @@
1
1
  import { html } from 'lit-element';
2
2
  import { unsafeHTML } from 'lit-html/directives/unsafe-html';
3
- import marked from 'marked';
3
+ import { marked } from 'marked';
4
4
  import '~/components/api-request';
5
5
  import '~/components/api-response';
6
6
  import codeSamplesTemplate from '~/templates/code-samples-template';
@@ -1,6 +1,6 @@
1
1
  import { html } from 'lit-element';
2
2
  import { unsafeHTML } from 'lit-html/directives/unsafe-html';
3
- import marked from 'marked';
3
+ import { marked } from 'marked';
4
4
  import { rapidocApiKey } from '~/utils/common-utils';
5
5
  import { pathSecurityTemplate } from '~/templates/security-scheme-template';
6
6
  import codeSamplesTemplate from '~/templates/code-samples-template';
@@ -1,6 +1,6 @@
1
1
  import { html } from 'lit-element';
2
2
  import { unsafeHTML } from 'lit-html/directives/unsafe-html';
3
- import marked from 'marked';
3
+ import { marked } from 'marked';
4
4
  import { expandedEndpointBodyTemplate } from '~/templates/expanded-endpoint-template';
5
5
  import '~/components/api-request';
6
6
  import '~/components/api-response';
@@ -52,7 +52,7 @@ export default function headerTemplate() {
52
52
  `
53
53
  }
54
54
 
55
- ${(this.allowSearch === 'false' || this.allowAdvancedSearch === 'false' || 'read focused'.includes(this.renderStyle))
55
+ ${(this.allowAdvancedSearch === 'false' || 'read focused'.includes(this.renderStyle))
56
56
  ? ''
57
57
  : html`
58
58
  <button class="m-btn primary only-large-screen" part="btn btn-fill btn-search" style="margin-left:10px;" @click="${this.onShowSearchModalClicked}">
@@ -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
  // Templates
@@ -49,7 +49,7 @@ function jsonSchemaBodyTemplate() {
49
49
  ${this.showInfo === 'true' ? overviewTemplate.call(this) : ''}
50
50
  <div style="font-size:var(--font-size-regular);">
51
51
  ${this.resolvedSpec.schemaAndExamples.map((jSchemaBody) => {
52
- const examplesObj = generateExample(jSchemaBody.examples, jSchemaBody.example, jSchemaBody.schema, 'json', true, false, 'json', true);
52
+ const examplesObj = generateExample(jSchemaBody.schema, 'json', jSchemaBody.examples, jSchemaBody.example, true, false, 'json', true);
53
53
  jSchemaBody.selectedExample = examplesObj[0]?.exampleId;
54
54
  return html`
55
55
  <section id='${jSchemaBody.elementId}' class='json-schema-and-example regular-font' style="display:flex; flex-direction: column; border:1px solid var(--border-color); margin-bottom:32px; border-top: 5px solid var(--border-color)">
@@ -60,12 +60,12 @@ function jsonSchemaBodyTemplate() {
60
60
  <div style="display:flex; flex-direction: row; gap:16px;">
61
61
  <div class="json-schema-def" style="flex:1; padding:16px 0 16px 16px; ">
62
62
  <schema-tree
63
- .data = '${schemaInObjectNotation(jSchemaBody.schema, {})}'
63
+ .data = "${schemaInObjectNotation(jSchemaBody.schema, {})}"
64
64
  schema-expand-level = "${this.schemaExpandLevel}"
65
65
  schema-description-expanded = "${this.schemaDescriptionExpanded}"
66
66
  allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}",
67
- schema-hide-read-only = false
68
- schema-hide-write-only = false
67
+ schema-hide-read-only = "false"
68
+ schema-hide-write-only = "false"
69
69
  > </schema-tree>
70
70
  </div>
71
71
  <div class="json-schema-example-panel" style="width:400px; background-color: var(--input-bg); padding:16px 0 16px 16px; border-left: 1px dashed var(--border-color);">
@@ -81,9 +81,9 @@ function jsonSchemaBodyTemplate() {
81
81
  }
82
82
  ${examplesObj.map((v) => html`
83
83
  <json-tree
84
- .data="${v.exampleValue}"
85
- data-example = '${v.exampleId}'
86
- class = 'example'
84
+ .data = "${v.exampleValue}"
85
+ data-example = "${v.exampleId}"
86
+ class = "example"
87
87
  style = "margin-top:16px; display: ${v.exampleId === jSchemaBody.selectedExample ? 'flex' : 'none'}"
88
88
  ></json-tree>`)
89
89
  }
@@ -6,7 +6,7 @@ import focusedEndpointTemplate from '~/templates/focused-endpoint-template';
6
6
  import overviewTemplate from '~/templates/overview-template';
7
7
  import endpointTemplate from '~/templates/endpoint-template';
8
8
  import serverTemplate from '~/templates/server-template';
9
- import securitySchemeTemplate from '~/templates/security-scheme-template';
9
+ import securitySchemeTemplate, { recoverPersistedApiKeys } from '~/templates/security-scheme-template';
10
10
  import headerTemplate from '~/templates/header-template';
11
11
  import navbarTemplate from '~/templates/navbar-template';
12
12
  import advancedSearchTemplate from '~/templates/advance-search-template';
@@ -17,6 +17,9 @@ export default function mainBodyTemplate(isMini = false, showExpandCollapse = tr
17
17
  if (!this.resolvedSpec) {
18
18
  return '';
19
19
  }
20
+ if (this.persistAuth === 'true') {
21
+ recoverPersistedApiKeys.call(this);
22
+ }
20
23
  const newTheme = {
21
24
  bg1: isValidHexColor(this.bgColor) ? this.bgColor : '',
22
25
  fg1: isValidHexColor(this.textColor) ? this.textColor : '',
@@ -1,5 +1,5 @@
1
1
  import { html } from 'lit-element';
2
- import marked from 'marked';
2
+ import { marked } from 'marked';
3
3
  import { pathIsInSearch } from '~/utils/common-utils';
4
4
 
5
5
  export function expandCollapseNavBarTag(navLinkEl, action = 'toggle') {
@@ -50,7 +50,7 @@ export default function navbarTemplate() {
50
50
  ${(this.allowSearch === 'false' && this.allowAdvancedSearch === 'false')
51
51
  ? ''
52
52
  : html`
53
- <div style="display:flex; flex-direction:row; justify-content:center; align-items:center; padding:24px; ${this.allowAdvancedSearch === 'false' ? 'border-bottom: 1px solid var(--nav-hover-bg-color)' : ''}">
53
+ <div style="display:flex; flex-direction:row; justify-content:center; align-items:center; padding:8px 24px 12px 24px; ${this.allowAdvancedSearch === 'false' ? 'border-bottom: 1px solid var(--nav-hover-bg-color)' : ''}">
54
54
  ${this.allowSearch === 'false'
55
55
  ? ''
56
56
  : html`
@@ -1,6 +1,6 @@
1
1
  import { html } from 'lit-element';
2
2
  import { unsafeHTML } from 'lit-html/directives/unsafe-html';
3
- import marked from 'marked';
3
+ import { marked } from 'marked';
4
4
  import { downloadResource, viewResource } from '~/utils/common-utils';
5
5
 
6
6
  /* eslint-disable indent */
@@ -51,7 +51,10 @@ export default function overviewTemplate() {
51
51
  ? html`
52
52
  <div style="display:flex; margin:12px 0; gap:8px; justify-content: start;">
53
53
  <button class="m-btn thin-border" style="width:170px" part="btn btn-outline" @click='${(e) => { downloadResource(this.specUrl, 'openapi-spec', e); }}'>Download OpenAPI spec</button>
54
- <button class="m-btn thin-border" style="width:200px" part="btn btn-outline" @click='${(e) => { viewResource(this.specUrl, e); }}'>View OpenAPI spec (New Tab)</button>
54
+ ${this.specUrl?.trim().toLowerCase().endsWith('json')
55
+ ? html`<button class="m-btn thin-border" style="width:200px" part="btn btn-outline" @click='${(e) => { viewResource(this.specUrl, e); }}'>View OpenAPI spec (New Tab)</button>`
56
+ : ''
57
+ }
55
58
  </div>`
56
59
  : ''
57
60
  }
@@ -1,11 +1,13 @@
1
1
  /* eslint-disable arrow-body-style */
2
2
  import { html } from 'lit-element';
3
3
  import { unsafeHTML } from 'lit-html/directives/unsafe-html';
4
- import marked from 'marked';
4
+ import { marked } from 'marked';
5
5
 
6
6
  const codeVerifier = '731DB1C3F7EA533B85E29492D26AA-1234567890-1234567890';
7
7
  const codeChallenge = '4FatVDBJKPAo4JgLLaaQFMUcQPn5CrPRvLlaob9PTYc'; // Base64 encoded SHA-256
8
8
 
9
+ const localStorageKey = 'rapidoc';
10
+
9
11
  export function applyApiKey(securitySchemeId, username = '', password = '', providedApikeyVal = '') {
10
12
  const securityObj = this.resolvedSpec.securitySchemes?.find((v) => (v.securitySchemeId === securitySchemeId));
11
13
  if (!securityObj) {
@@ -17,7 +19,8 @@ export function applyApiKey(securitySchemeId, username = '', password = '', prov
17
19
  finalApiKeyValue = `Basic ${btoa(`${username}:${password}`)}`;
18
20
  }
19
21
  } else if (providedApikeyVal) {
20
- finalApiKeyValue = `${securityObj.scheme?.toLowerCase() === 'bearer' ? 'Bearer' : ''} ${providedApikeyVal}`;
22
+ securityObj.value = providedApikeyVal;
23
+ finalApiKeyValue = `${securityObj.scheme?.toLowerCase() === 'bearer' ? 'Bearer ' : ''}${providedApikeyVal}`;
21
24
  }
22
25
  if (finalApiKeyValue) {
23
26
  securityObj.finalKeyValue = finalApiKeyValue;
@@ -37,6 +40,21 @@ export function onClearAllApiKeys() {
37
40
  this.requestUpdate();
38
41
  }
39
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
+
40
58
  function onApiKeyChange(securitySchemeId) {
41
59
  let apiKeyValue = '';
42
60
  const securityObj = this.resolvedSpec.securitySchemes.find((v) => (v.securitySchemeId === securitySchemeId));
@@ -51,12 +69,17 @@ function onApiKeyChange(securitySchemeId) {
51
69
  apiKeyValue = trEl.querySelector('.api-key-input').value.trim();
52
70
  applyApiKey.call(this, securitySchemeId, '', '', apiKeyValue);
53
71
  }
72
+ if (this.persistAuth === 'true') {
73
+ const rapidocLs = getPersistedApiKeys.call(this);
74
+ rapidocLs[securitySchemeId] = securityObj;
75
+ setPersistedApiKeys.call(this, rapidocLs);
76
+ }
54
77
  }
55
78
  }
56
79
  }
57
80
 
58
81
  // Updates the OAuth Access Token (API key), so it reflects in UI and gets used in TRY calls
59
- function updateOAuthKey(securitySchemeId, tokenType = 'Bearer', accessToken) {
82
+ function updateOAuthKey(securitySchemeId, accessToken, tokenType = 'Bearer') {
60
83
  const securityObj = this.resolvedSpec.securitySchemes.find((v) => (v.securitySchemeId === securitySchemeId));
61
84
  securityObj.finalKeyValue = `${(tokenType.toLowerCase() === 'bearer' ? 'Bearer' : (tokenType.toLowerCase() === 'mac' ? 'MAC' : tokenType))} ${accessToken}`;
62
85
  this.requestUpdate();
@@ -64,7 +87,7 @@ function updateOAuthKey(securitySchemeId, tokenType = 'Bearer', accessToken) {
64
87
 
65
88
  /* eslint-disable no-console */
66
89
  // Gets Access-Token in exchange of Authorization Code
67
- 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) {
68
91
  const respDisplayEl = authFlowDivEl ? authFlowDivEl.querySelector('.oauth-resp-display') : undefined;
69
92
  const urlFormParams = new URLSearchParams();
70
93
  const headers = new Headers();
@@ -95,7 +118,7 @@ async function fetchAccessToken(tokenUrl, clientId, clientSecret, redirectUrl, g
95
118
  const tokenResp = await resp.json();
96
119
  if (resp.ok) {
97
120
  if (tokenResp.token_type && tokenResp.access_token) {
98
- updateOAuthKey.call(this, securitySchemeId, tokenResp.token_type, tokenResp.access_token);
121
+ updateOAuthKey.call(this, securitySchemeId, tokenResp.access_token, tokenResp.token_type);
99
122
  if (respDisplayEl) {
100
123
  respDisplayEl.innerHTML = '<span style="color:var(--green)">Access Token Received</span>';
101
124
  }
@@ -131,10 +154,10 @@ async function onWindowMessageEvent(msgEvent, winObj, tokenUrl, clientId, client
131
154
  if (msgEvent.data) {
132
155
  if (msgEvent.data.responseType === 'code') {
133
156
  // Authorization Code flow
134
- 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);
135
158
  } else if (msgEvent.data.responseType === 'token') {
136
159
  // Implicit flow
137
- 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);
138
161
  }
139
162
  }
140
163
  }
@@ -218,11 +241,11 @@ async function onInvokeOAuthFlow(securitySchemeId, flowType, authUrl, tokenUrl,
218
241
  } else if (flowType === 'clientCredentials') {
219
242
  grantType = 'client_credentials';
220
243
  const selectedScopes = checkedScopeEls.map((v) => v.value).join(' ');
221
- 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);
222
245
  } else if (flowType === 'password') {
223
246
  grantType = 'password';
224
247
  const selectedScopes = checkedScopeEls.map((v) => v.value).join(' ');
225
- 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);
226
249
  }
227
250
  }
228
251
  /* eslint-enable no-console */
@@ -339,6 +362,20 @@ function oAuthFlowTemplate(flowName, clientId, clientSecret, securitySchemeId, a
339
362
  `;
340
363
  }
341
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
+
342
379
  export default function securitySchemeTemplate() {
343
380
  if (!this.resolvedSpec) { return ''; }
344
381
  const providedApiKeys = this.resolvedSpec.securitySchemes?.filter((v) => (v.finalKeyValue));
@@ -369,7 +406,7 @@ export default function securitySchemeTemplate() {
369
406
  ${v.finalKeyValue
370
407
  ? html`
371
408
  <span class='blue-text'> ${v.finalKeyValue ? 'Key Applied' : ''} </span>
372
- <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>
373
410
  `
374
411
  : ''
375
412
  }
@@ -427,9 +464,7 @@ export default function securitySchemeTemplate() {
427
464
  ? html`
428
465
  <tr>
429
466
  <td style="border:none; padding-left:48px">
430
- ${Object.keys(v.flows).map((f) => oAuthFlowTemplate.call(
431
- this, f, v['x-client-id'], v['x-client-secret'], v.securitySchemeId, v.flows[f],
432
- ))}
467
+ ${Object.keys(v.flows).map((f) => oAuthFlowTemplate.call(this, f, v['x-client-id'], v['x-client-secret'], v.securitySchemeId, v.flows[f]))}
433
468
  </td>
434
469
  </tr>
435
470
  `
@@ -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