rapidoc 9.3.3 → 9.3.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/.github/workflow/words.yaml +22 -0
- package/dist/rapidoc-min.js +815 -709
- package/dist/rapidoc-min.js.LICENSE.txt +8 -7
- package/dist/rapidoc-min.js.LICENSE.txt.gz +0 -0
- package/dist/rapidoc-min.js.gz +0 -0
- package/dist/rapidoc-min.js.map +1 -1
- package/dist/rapidoc-min.js.map.gz +0 -0
- package/dist/rapidoc.js +17680 -31489
- package/dist/report.html +5 -4
- package/package.json +18 -18
- package/src/components/api-request.js +218 -112
- package/src/components/api-response.js +11 -10
- package/src/components/json-tree.js +17 -12
- package/src/components/schema-table.js +47 -39
- package/src/components/schema-tree.js +55 -52
- package/src/components/tag-input.js +7 -4
- package/src/json-schema-viewer.js +1 -1
- package/src/rapidoc-mini.js +1 -1
- package/src/rapidoc.js +98 -30
- package/src/styles/endpoint-styles.js +1 -1
- package/src/styles/info-styles.js +2 -1
- package/src/styles/input-styles.js +2 -16
- package/src/styles/nav-styles.js +57 -10
- package/src/styles/schema-styles.js +27 -15
- package/src/styles/tab-styles.js +1 -0
- package/src/templates/callback-template.js +3 -3
- package/src/templates/code-samples-template.js +1 -0
- package/src/templates/components-template.js +3 -5
- package/src/templates/endpoint-template.js +38 -25
- package/src/templates/expanded-endpoint-template.js +57 -8
- package/src/templates/focused-endpoint-template.js +4 -1
- package/src/templates/header-template.js +2 -2
- package/src/templates/json-schema-viewer-template.js +2 -1
- package/src/templates/main-body-template.js +19 -18
- package/src/templates/navbar-template.js +98 -81
- package/src/templates/overview-template.js +1 -1
- package/src/templates/security-scheme-template.js +48 -25
- package/src/templates/server-template.js +1 -1
- package/src/utils/common-utils.js +1 -1
- package/src/utils/schema-utils.js +161 -63
- package/src/utils/spec-parser.js +1 -0
- package/src/utils/theme.js +9 -3
- package/webpack.config.js +2 -1
|
@@ -60,8 +60,10 @@ export default class ApiRequest extends LitElement {
|
|
|
60
60
|
responseHeaders: { type: String, attribute: false },
|
|
61
61
|
responseStatus: { type: String, attribute: false },
|
|
62
62
|
responseUrl: { type: String, attribute: false },
|
|
63
|
+
curlSyntax: { type: String, attribute: false },
|
|
63
64
|
fillRequestFieldsWithExample: { type: String, attribute: 'fill-request-fields-with-example' },
|
|
64
65
|
allowTry: { type: String, attribute: 'allow-try' },
|
|
66
|
+
showCurlBeforeTry: { type: String, attribute: 'show-curl-before-try' },
|
|
65
67
|
renderStyle: { type: String, attribute: 'render-style' },
|
|
66
68
|
schemaStyle: { type: String, attribute: 'schema-style' },
|
|
67
69
|
activeSchemaTab: { type: String, attribute: 'active-schema-tab' },
|
|
@@ -98,7 +100,9 @@ export default class ApiRequest extends LitElement {
|
|
|
98
100
|
PrismStyles,
|
|
99
101
|
css`
|
|
100
102
|
*, *:before, *:after { box-sizing: border-box; }
|
|
101
|
-
|
|
103
|
+
:where(button, input[type="checkbox"], [tabindex="0"]):focus-visible { box-shadow: var(--focus-shadow); }
|
|
104
|
+
:where(input[type="text"], input[type="password"], select, textarea):focus-visible { border-color: var(--primary-color); }
|
|
105
|
+
tag-input:focus-within { outline: 1px solid;}
|
|
102
106
|
.read-mode {
|
|
103
107
|
margin-top: 24px;
|
|
104
108
|
}
|
|
@@ -213,20 +217,27 @@ export default class ApiRequest extends LitElement {
|
|
|
213
217
|
`;
|
|
214
218
|
}
|
|
215
219
|
|
|
216
|
-
|
|
217
|
-
|
|
220
|
+
async updated() {
|
|
221
|
+
if (this.showCurlBeforeTry === 'true') {
|
|
222
|
+
this.applyCURLSyntax(this.shadowRoot);
|
|
223
|
+
}
|
|
224
|
+
|
|
218
225
|
// In focused mode after rendering the request component, update the text-areas(which contains examples) using
|
|
219
226
|
// the original values from hidden textareas
|
|
220
227
|
// This is done coz, user may update the dom by editing the textarea's and once the DOM is updated externally change detection wont happen, therefore update the values manually
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
228
|
+
|
|
229
|
+
// if (this.renderStyle === 'focused') {
|
|
230
|
+
// if (changedProperties.size === 1 && changedProperties.has('activeSchemaTab')) {
|
|
231
|
+
// // dont update example as only tabs is switched
|
|
232
|
+
// } else {
|
|
233
|
+
// this.requestUpdate();
|
|
234
|
+
// }
|
|
235
|
+
// }
|
|
236
|
+
|
|
237
|
+
if (this.webhook === 'true') {
|
|
238
|
+
this.allowTry = 'false';
|
|
227
239
|
}
|
|
228
240
|
}
|
|
229
|
-
*/
|
|
230
241
|
|
|
231
242
|
async saveExampleState() {
|
|
232
243
|
if (this.renderStyle === 'focused') {
|
|
@@ -259,28 +270,23 @@ export default class ApiRequest extends LitElement {
|
|
|
259
270
|
}
|
|
260
271
|
}
|
|
261
272
|
|
|
273
|
+
/* eslint-disable indent */
|
|
262
274
|
renderExample(example, paramType, paramName) {
|
|
263
275
|
return html`
|
|
264
276
|
${paramType === 'array' ? '[' : ''}
|
|
265
277
|
<a
|
|
266
278
|
part="anchor anchor-param-example"
|
|
279
|
+
style="display:inline-block; min-width:24px; text-align:center"
|
|
267
280
|
class="${this.allowTry === 'true' ? '' : 'inactive-link'}"
|
|
268
281
|
data-example-type="${paramType === 'array' ? paramType : 'string'}"
|
|
269
282
|
data-example="${example.value && Array.isArray(example.value) ? example.value?.join('~|~') : example.value || ''}"
|
|
270
283
|
@click="${(e) => {
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}"
|
|
281
|
-
>
|
|
282
|
-
${example.value && Array.isArray(example.value) ? example.value?.join(', ') : example.value || '∅'}
|
|
283
|
-
</a>
|
|
284
|
+
const inputEl = e.target.closest('table').querySelector(`[data-pname="${paramName}"]`);
|
|
285
|
+
if (inputEl) {
|
|
286
|
+
inputEl.value = e.target.dataset.exampleType === 'array' ? e.target.dataset.example.split('~|~') : e.target.dataset.example;
|
|
287
|
+
}
|
|
288
|
+
}}"
|
|
289
|
+
> ${example.printableValue || example.value} </a>
|
|
284
290
|
${paramType === 'array' ? '] ' : ''}
|
|
285
291
|
`;
|
|
286
292
|
}
|
|
@@ -303,17 +309,15 @@ export default class ApiRequest extends LitElement {
|
|
|
303
309
|
</ul>`;
|
|
304
310
|
}
|
|
305
311
|
|
|
306
|
-
/* eslint-disable indent */
|
|
307
|
-
|
|
308
312
|
exampleListTemplate(paramName, paramType, exampleList = []) {
|
|
309
|
-
return html` ${
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
+
return html` ${
|
|
314
|
+
exampleList.length > 0
|
|
315
|
+
? html`<span style="font-weight:bold">Examples: </span>
|
|
316
|
+
${anyExampleWithSummaryOrDescription(exampleList)
|
|
313
317
|
? this.renderLongFormatExamples(exampleList, paramType, paramName)
|
|
314
|
-
: this.renderShortFormatExamples(exampleList, paramType, paramName)
|
|
315
|
-
|
|
316
|
-
|
|
318
|
+
: this.renderShortFormatExamples(exampleList, paramType, paramName)}`
|
|
319
|
+
: ''
|
|
320
|
+
}`;
|
|
317
321
|
}
|
|
318
322
|
|
|
319
323
|
inputParametersTemplate(paramType) {
|
|
@@ -336,7 +340,7 @@ export default class ApiRequest extends LitElement {
|
|
|
336
340
|
for (const param of filteredParams) {
|
|
337
341
|
const [declaredParamSchema, serializeStyle, mimeTypeElem] = getSchemaFromParam(param);
|
|
338
342
|
if (!declaredParamSchema) {
|
|
339
|
-
continue;
|
|
343
|
+
continue;
|
|
340
344
|
}
|
|
341
345
|
const paramSchema = getTypeInfo(declaredParamSchema);
|
|
342
346
|
if (!paramSchema) {
|
|
@@ -367,7 +371,7 @@ export default class ApiRequest extends LitElement {
|
|
|
367
371
|
|| nestExampleIfPresent(param.example)
|
|
368
372
|
|| nestExampleIfPresent(mimeTypeElem?.example)
|
|
369
373
|
|| mimeTypeElem?.examples
|
|
370
|
-
|| paramSchema.examples
|
|
374
|
+
|| nestExampleIfPresent(paramSchema.examples)
|
|
371
375
|
|| nestExampleIfPresent(paramSchema.example)
|
|
372
376
|
),
|
|
373
377
|
paramSchema.type,
|
|
@@ -451,6 +455,10 @@ export default class ApiRequest extends LitElement {
|
|
|
451
455
|
spellcheck = "false"
|
|
452
456
|
.textContent="${param['x-fill-example'] === 'no' ? '' : live(this.fillRequestFieldsWithExample === 'true' ? example.exampleVal : '')}"
|
|
453
457
|
style = "resize:vertical; width:100%; height: ${'read focused'.includes(this.renderStyle) ? '180px' : '120px'};"
|
|
458
|
+
@input=${(e) => {
|
|
459
|
+
const requestPanelEl = this.getRequestPanel(e);
|
|
460
|
+
this.liveCURLSyntaxUpdate(requestPanelEl);
|
|
461
|
+
}}
|
|
454
462
|
></textarea>
|
|
455
463
|
</div>`
|
|
456
464
|
: html`
|
|
@@ -461,12 +469,12 @@ export default class ApiRequest extends LitElement {
|
|
|
461
469
|
.data = '${schemaAsObj}'
|
|
462
470
|
schema-expand-level = "${this.schemaExpandLevel}"
|
|
463
471
|
schema-description-expanded = "${this.schemaDescriptionExpanded}"
|
|
464
|
-
allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}"
|
|
472
|
+
allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}"
|
|
465
473
|
schema-hide-read-only = "${this.schemaHideReadOnly.includes(this.method)}"
|
|
466
474
|
schema-hide-write-only = "${this.schemaHideWriteOnly.includes(this.method)}"
|
|
467
|
-
exportparts = "btn:btn, btn-fill:btn-fill, btn-outline:btn-outline, btn-try:btn-try, btn-clear:btn-clear, btn-clear-resp:btn-clear-resp,
|
|
468
|
-
|
|
469
|
-
|
|
475
|
+
exportparts = "wrap-request-btn:wrap-request-btn, btn:btn, btn-fill:btn-fill, btn-outline:btn-outline, btn-try:btn-try, btn-clear:btn-clear, btn-clear-resp:btn-clear-resp,
|
|
476
|
+
file-input:file-input, textbox:textbox, textbox-param:textbox-param, textarea:textarea, textarea-param:textarea-param,
|
|
477
|
+
anchor:anchor, anchor-param-example:anchor-param-example"
|
|
470
478
|
> </schema-tree>
|
|
471
479
|
</div>`
|
|
472
480
|
}
|
|
@@ -482,6 +490,10 @@ export default class ApiRequest extends LitElement {
|
|
|
482
490
|
data-x-fill-example = "${param['x-fill-example'] || 'yes'}"
|
|
483
491
|
data-array="false"
|
|
484
492
|
.value="${param['x-fill-example'] === 'no' ? '' : live(this.fillRequestFieldsWithExample === 'true' ? example.exampleVal : '')}"
|
|
493
|
+
@input=${(e) => {
|
|
494
|
+
const requestPanelEl = this.getRequestPanel(e);
|
|
495
|
+
this.liveCURLSyntaxUpdate(requestPanelEl);
|
|
496
|
+
}}
|
|
485
497
|
/>`
|
|
486
498
|
}
|
|
487
499
|
</td>`
|
|
@@ -530,18 +542,18 @@ export default class ApiRequest extends LitElement {
|
|
|
530
542
|
return html`
|
|
531
543
|
<div class="table-title top-gap">${title}</div>
|
|
532
544
|
<div style="display:block; overflow-x:auto; max-width:100%;">
|
|
533
|
-
<table class="m-table" style="width:100%; word-break:break-word;">
|
|
545
|
+
<table role="presentation" class="m-table" style="width:100%; word-break:break-word;">
|
|
534
546
|
${tableRows}
|
|
535
547
|
</table>
|
|
536
548
|
</div>`;
|
|
537
549
|
}
|
|
538
550
|
|
|
539
|
-
// This method is called before navigation change in
|
|
540
|
-
async
|
|
551
|
+
// This method is called before navigation change in focused mode
|
|
552
|
+
async beforeNavigationFocusedMode() {
|
|
541
553
|
// this.saveExampleState();
|
|
542
554
|
}
|
|
543
555
|
|
|
544
|
-
// This method is called after navigation change in
|
|
556
|
+
// This method is called after navigation change in focused mode
|
|
545
557
|
async afterNavigationFocusedMode() {
|
|
546
558
|
this.selectedRequestBodyType = '';
|
|
547
559
|
this.selectedRequestBodyExample = '';
|
|
@@ -557,6 +569,9 @@ export default class ApiRequest extends LitElement {
|
|
|
557
569
|
const readOnlyExampleEl = selectEl.closest('.example-panel').querySelector('.request-body-param');
|
|
558
570
|
const userInputExampleTextareaEl = selectEl.closest('.example-panel').querySelector('.request-body-param-user-input');
|
|
559
571
|
userInputExampleTextareaEl.value = readOnlyExampleEl.innerText;
|
|
572
|
+
|
|
573
|
+
const requestPanelEl = this.getRequestPanel({ target: selectEl });
|
|
574
|
+
this.liveCURLSyntaxUpdate(requestPanelEl);
|
|
560
575
|
}, 0, exampleDropdownEl);
|
|
561
576
|
}
|
|
562
577
|
|
|
@@ -672,6 +687,10 @@ export default class ApiRequest extends LitElement {
|
|
|
672
687
|
data-example-format = "${v.exampleFormat}"
|
|
673
688
|
style="width:100%; resize:vertical;"
|
|
674
689
|
.textContent = "${this.fillRequestFieldsWithExample === 'true' ? (v.exampleFormat === 'text' ? v.exampleValue : JSON.stringify(v.exampleValue, null, 2)) : ''}"
|
|
690
|
+
@input=${(e) => {
|
|
691
|
+
const requestPanelEl = this.getRequestPanel(e);
|
|
692
|
+
this.liveCURLSyntaxUpdate(requestPanelEl);
|
|
693
|
+
}}
|
|
675
694
|
></textarea>
|
|
676
695
|
</div>
|
|
677
696
|
`)}
|
|
@@ -717,7 +736,7 @@ export default class ApiRequest extends LitElement {
|
|
|
717
736
|
.data = '${schemaAsObj}'
|
|
718
737
|
schema-expand-level = "${this.schemaExpandLevel}"
|
|
719
738
|
schema-description-expanded = "${this.schemaDescriptionExpanded}"
|
|
720
|
-
allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}"
|
|
739
|
+
allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}"
|
|
721
740
|
schema-hide-read-only = "${this.schemaHideReadOnly}"
|
|
722
741
|
schema-hide-write-only = "${this.schemaHideWriteOnly}"
|
|
723
742
|
exportparts = "schema-description:schema-description, schema-multiline-toggle:schema-multiline-toggle"
|
|
@@ -732,7 +751,7 @@ export default class ApiRequest extends LitElement {
|
|
|
732
751
|
.data = "${schemaAsObj}"
|
|
733
752
|
schema-expand-level = "${this.schemaExpandLevel}"
|
|
734
753
|
schema-description-expanded = "${this.schemaDescriptionExpanded}"
|
|
735
|
-
allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}"
|
|
754
|
+
allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}"
|
|
736
755
|
schema-hide-read-only = "${this.schemaHideReadOnly}"
|
|
737
756
|
schema-hide-write-only = "${this.schemaHideWriteOnly}"
|
|
738
757
|
exportparts = "schema-description:schema-description, schema-multiline-toggle:schema-multiline-toggle"
|
|
@@ -965,7 +984,7 @@ export default class ApiRequest extends LitElement {
|
|
|
965
984
|
}`);
|
|
966
985
|
}
|
|
967
986
|
return html`
|
|
968
|
-
<table style="width:100%;" class="m-table">
|
|
987
|
+
<table role="presentation" style="width:100%;" class="m-table">
|
|
969
988
|
${formDataTableRows}
|
|
970
989
|
</table>
|
|
971
990
|
`;
|
|
@@ -985,6 +1004,15 @@ export default class ApiRequest extends LitElement {
|
|
|
985
1004
|
`;
|
|
986
1005
|
}
|
|
987
1006
|
|
|
1007
|
+
curlSyntaxTemplate(display = 'flex') {
|
|
1008
|
+
return html`
|
|
1009
|
+
<div class="col m-markdown" style="flex:1; display:${display}; position:relative; max-width: 100%;">
|
|
1010
|
+
<button class="toolbar-btn" style = "position:absolute; top:12px; right:8px" @click='${(e) => { copyToClipboard(this.curlSyntax.replace(/\\$/, ''), e); }}' part="btn btn-fill"> Copy </button>
|
|
1011
|
+
<pre style="white-space:pre"><code>${unsafeHTML(Prism.highlight(this.curlSyntax.trim().replace(/\\$/, ''), Prism.languages.shell, 'shell'))}</code></pre>
|
|
1012
|
+
</div>
|
|
1013
|
+
`;
|
|
1014
|
+
}
|
|
1015
|
+
|
|
988
1016
|
apiResponseTabTemplate() {
|
|
989
1017
|
let responseFormat = '';
|
|
990
1018
|
let responseContent = '';
|
|
@@ -1017,7 +1045,9 @@ export default class ApiRequest extends LitElement {
|
|
|
1017
1045
|
}}">
|
|
1018
1046
|
<button class="tab-btn ${this.activeResponseTab === 'response' ? 'active' : ''}" data-tab = 'response' > RESPONSE</button>
|
|
1019
1047
|
<button class="tab-btn ${this.activeResponseTab === 'headers' ? 'active' : ''}" data-tab = 'headers' > RESPONSE HEADERS</button>
|
|
1020
|
-
|
|
1048
|
+
${this.showCurlBeforeTry === 'true'
|
|
1049
|
+
? ''
|
|
1050
|
+
: html`<button class="tab-btn ${this.activeResponseTab === 'curl' ? 'active' : ''}" data-tab = 'curl'>CURL</button>`}
|
|
1021
1051
|
</div>
|
|
1022
1052
|
${this.responseIsBlob
|
|
1023
1053
|
? html`
|
|
@@ -1040,10 +1070,7 @@ export default class ApiRequest extends LitElement {
|
|
|
1040
1070
|
<button class="toolbar-btn" style = "position:absolute; top:12px; right:8px" @click='${(e) => { copyToClipboard(this.responseHeaders, e); }}' part="btn btn-fill"> Copy </button>
|
|
1041
1071
|
<pre style="white-space:pre"><code>${unsafeHTML(Prism.highlight(this.responseHeaders, Prism.languages.css, 'css'))}</code></pre>
|
|
1042
1072
|
</div>
|
|
1043
|
-
|
|
1044
|
-
<button class="toolbar-btn" style = "position:absolute; top:12px; right:8px" @click='${(e) => { copyToClipboard(this.curlSyntax.replace(/\\$/, ''), e); }}' part="btn btn-fill"> Copy </button>
|
|
1045
|
-
<pre style="white-space:pre"><code>${unsafeHTML(Prism.highlight(this.curlSyntax.trim().replace(/\\$/, ''), Prism.languages.shell, 'shell'))}</code></pre>
|
|
1046
|
-
</div>
|
|
1073
|
+
${this.showCurlBeforeTry === 'true' ? '' : this.curlSyntaxTemplate(this.activeResponseTab === 'curl' ? 'flex' : 'none')}
|
|
1047
1074
|
</div>`;
|
|
1048
1075
|
}
|
|
1049
1076
|
|
|
@@ -1073,7 +1100,7 @@ export default class ApiRequest extends LitElement {
|
|
|
1073
1100
|
`;
|
|
1074
1101
|
|
|
1075
1102
|
return html`
|
|
1076
|
-
<div style="display:flex; align-items:flex-end; margin:16px 0; font-size:var(--font-size-small);">
|
|
1103
|
+
<div style="display:flex; align-items:flex-end; margin:16px 0; font-size:var(--font-size-small);" part="wrap-request-btn">
|
|
1077
1104
|
<div class="hide-in-small-screen" style="flex-direction:column; margin:0; width:calc(100% - 60px);">
|
|
1078
1105
|
<div style="display:flex; flex-direction:row; align-items:center; overflow:hidden;">
|
|
1079
1106
|
${selectedServerHtml}
|
|
@@ -1108,6 +1135,9 @@ export default class ApiRequest extends LitElement {
|
|
|
1108
1135
|
}
|
|
1109
1136
|
<button class="m-btn primary thin-border" part="btn btn-try" @click="${this.onTryClick}">TRY</button>
|
|
1110
1137
|
</div>
|
|
1138
|
+
<div class="row" style="font-size:var(--font-size-small); margin:5px 0">
|
|
1139
|
+
${this.showCurlBeforeTry === 'true' ? this.curlSyntaxTemplate() : ''}
|
|
1140
|
+
</div>
|
|
1111
1141
|
${this.responseMessage === '' ? '' : this.apiResponseTabTemplate()}
|
|
1112
1142
|
`;
|
|
1113
1143
|
}
|
|
@@ -1133,27 +1163,12 @@ export default class ApiRequest extends LitElement {
|
|
|
1133
1163
|
requestPanelInputEls.forEach((el) => { el.value = ''; });
|
|
1134
1164
|
}
|
|
1135
1165
|
|
|
1136
|
-
|
|
1137
|
-
// const me = this;
|
|
1138
|
-
const tryBtnEl = e.target;
|
|
1166
|
+
buildFetchURL(requestPanelEl) {
|
|
1139
1167
|
let fetchUrl;
|
|
1140
|
-
let curlUrl;
|
|
1141
|
-
let curl = '';
|
|
1142
|
-
let curlHeaders = '';
|
|
1143
|
-
let curlData = '';
|
|
1144
|
-
let curlForm = '';
|
|
1145
|
-
const respEl = this.closest('.expanded-req-resp-container, .req-resp-container')?.getElementsByTagName('api-response')[0];
|
|
1146
|
-
const acceptHeader = respEl?.selectedMimeType;
|
|
1147
|
-
const requestPanelEl = e.target.closest('.request-panel');
|
|
1148
1168
|
const pathParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='path']")];
|
|
1149
1169
|
const queryParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='query']")];
|
|
1150
1170
|
const queryParamObjTypeEls = [...requestPanelEl.querySelectorAll("[data-ptype='query-object']")];
|
|
1151
|
-
const headerParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='header']")];
|
|
1152
|
-
const requestBodyContainerEl = requestPanelEl.querySelector('.request-body-container');
|
|
1153
1171
|
fetchUrl = this.path;
|
|
1154
|
-
const fetchOptions = {
|
|
1155
|
-
method: this.method.toUpperCase(),
|
|
1156
|
-
};
|
|
1157
1172
|
// Generate URL using Path Params
|
|
1158
1173
|
pathParamEls.map((el) => {
|
|
1159
1174
|
fetchUrl = fetchUrl.replace(`{${el.dataset.pname}}`, encodeURIComponent(el.value));
|
|
@@ -1237,7 +1252,7 @@ export default class ApiRequest extends LitElement {
|
|
|
1237
1252
|
}
|
|
1238
1253
|
}
|
|
1239
1254
|
} catch (err) {
|
|
1240
|
-
console.
|
|
1255
|
+
console.error('RapiDoc: unable to parse %s into object', el.value); // eslint-disable-line no-console
|
|
1241
1256
|
}
|
|
1242
1257
|
if (queryParam.toString()) {
|
|
1243
1258
|
urlQueryParamsMap.set(el.dataset.pname, queryParam);
|
|
@@ -1246,7 +1261,6 @@ export default class ApiRequest extends LitElement {
|
|
|
1246
1261
|
}
|
|
1247
1262
|
let urlQueryParamString = '';
|
|
1248
1263
|
if (urlQueryParamsMap.size) {
|
|
1249
|
-
urlQueryParamString = '?';
|
|
1250
1264
|
urlQueryParamsMap.forEach((val, pname) => {
|
|
1251
1265
|
if (queryParamsWithReservedCharsAllowed.includes(pname)) {
|
|
1252
1266
|
urlQueryParamString += `${pname}=`;
|
|
@@ -1258,7 +1272,9 @@ export default class ApiRequest extends LitElement {
|
|
|
1258
1272
|
});
|
|
1259
1273
|
urlQueryParamString = urlQueryParamString.slice(0, -1);
|
|
1260
1274
|
}
|
|
1261
|
-
|
|
1275
|
+
if (urlQueryParamString.length !== 0) {
|
|
1276
|
+
fetchUrl = `${fetchUrl}${fetchUrl.includes('?') ? '&' : '?'}${urlQueryParamString}`;
|
|
1277
|
+
}
|
|
1262
1278
|
|
|
1263
1279
|
// Add authentication Query-Param if provided
|
|
1264
1280
|
this.api_keys
|
|
@@ -1267,23 +1283,21 @@ export default class ApiRequest extends LitElement {
|
|
|
1267
1283
|
fetchUrl = `${fetchUrl}${fetchUrl.includes('?') ? '&' : '?'}${v.name}=${encodeURIComponent(v.finalKeyValue)}`;
|
|
1268
1284
|
});
|
|
1269
1285
|
|
|
1270
|
-
// Final URL for API call
|
|
1271
1286
|
fetchUrl = `${this.serverUrl.replace(/\/$/, '')}${fetchUrl}`;
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1287
|
+
return fetchUrl;
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1290
|
+
buildFetchHeaders(requestPanelEl) {
|
|
1291
|
+
const respEl = this.closest('.expanded-req-resp-container, .req-resp-container')?.getElementsByTagName('api-response')[0];
|
|
1292
|
+
const headerParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='header']")];
|
|
1293
|
+
const requestBodyContainerEl = requestPanelEl.querySelector('.request-body-container');
|
|
1294
|
+
const acceptHeader = respEl?.selectedMimeType;
|
|
1279
1295
|
const reqHeaders = new Headers();
|
|
1280
1296
|
if (acceptHeader) {
|
|
1281
1297
|
// Uses the acceptHeader from Response panel
|
|
1282
1298
|
reqHeaders.append('Accept', acceptHeader);
|
|
1283
|
-
curlHeaders += ` -H "Accept: ${acceptHeader}" \\\n`;
|
|
1284
1299
|
} else if (this.accept) {
|
|
1285
1300
|
reqHeaders.append('Accept', this.accept);
|
|
1286
|
-
curlHeaders += ` -H "Accept: ${this.accept}" \\\n`;
|
|
1287
1301
|
}
|
|
1288
1302
|
|
|
1289
1303
|
// Add Authentication Header if provided
|
|
@@ -1291,18 +1305,32 @@ export default class ApiRequest extends LitElement {
|
|
|
1291
1305
|
.filter((v) => (v.in === 'header'))
|
|
1292
1306
|
.forEach((v) => {
|
|
1293
1307
|
reqHeaders.append(v.name, v.finalKeyValue);
|
|
1294
|
-
curlHeaders += ` -H "${v.name}: ${v.finalKeyValue}" \\\n`;
|
|
1295
1308
|
});
|
|
1296
1309
|
|
|
1297
1310
|
// Add Header Params
|
|
1298
1311
|
headerParamEls.map((el) => {
|
|
1299
1312
|
if (el.value) {
|
|
1300
1313
|
reqHeaders.append(el.dataset.pname, el.value);
|
|
1301
|
-
curlHeaders += ` -H "${el.dataset.pname}: ${el.value}" \\\n`;
|
|
1302
1314
|
}
|
|
1303
1315
|
});
|
|
1304
1316
|
|
|
1305
|
-
|
|
1317
|
+
if (requestBodyContainerEl) {
|
|
1318
|
+
const requestBodyType = requestBodyContainerEl.dataset.selectedRequestBodyType;
|
|
1319
|
+
// Common for all request-body
|
|
1320
|
+
if (!requestBodyType.includes('form-data')) {
|
|
1321
|
+
// For multipart/form-data dont set the content-type to allow creation of browser generated part boundaries
|
|
1322
|
+
reqHeaders.append('Content-Type', requestBodyType);
|
|
1323
|
+
}
|
|
1324
|
+
}
|
|
1325
|
+
|
|
1326
|
+
return reqHeaders;
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
buildFetchBodyOptions(requestPanelEl) {
|
|
1330
|
+
const requestBodyContainerEl = requestPanelEl.querySelector('.request-body-container');
|
|
1331
|
+
const fetchOptions = {
|
|
1332
|
+
method: this.method.toUpperCase(),
|
|
1333
|
+
};
|
|
1306
1334
|
if (requestBodyContainerEl) {
|
|
1307
1335
|
const requestBodyType = requestBodyContainerEl.dataset.selectedRequestBodyType;
|
|
1308
1336
|
if (requestBodyType.includes('form-urlencoded')) {
|
|
@@ -1328,7 +1356,6 @@ export default class ApiRequest extends LitElement {
|
|
|
1328
1356
|
formUrlDynParams.append(prop, JSON.stringify(tmpObj[prop]));
|
|
1329
1357
|
}
|
|
1330
1358
|
fetchOptions.body = formUrlDynParams;
|
|
1331
|
-
curlData = ` -d ${formUrlDynParams.toString()} \\\n`;
|
|
1332
1359
|
}
|
|
1333
1360
|
} else {
|
|
1334
1361
|
// url-encoded Form Params (regular)
|
|
@@ -1347,7 +1374,6 @@ export default class ApiRequest extends LitElement {
|
|
|
1347
1374
|
}
|
|
1348
1375
|
});
|
|
1349
1376
|
fetchOptions.body = formUrlParams;
|
|
1350
|
-
curlData = ` -d ${formUrlParams.toString()} \\\n`;
|
|
1351
1377
|
}
|
|
1352
1378
|
} else if (requestBodyType.includes('form-data')) {
|
|
1353
1379
|
const formDataParams = new FormData();
|
|
@@ -1356,15 +1382,10 @@ export default class ApiRequest extends LitElement {
|
|
|
1356
1382
|
if (el.dataset.array === 'false') {
|
|
1357
1383
|
if (el.type === 'file' && el.files[0]) {
|
|
1358
1384
|
formDataParams.append(el.dataset.pname, el.files[0], el.files[0].name);
|
|
1359
|
-
curlForm += ` -F "${el.dataset.pname}=@${el.files[0].name}" \\\n`;
|
|
1360
1385
|
} else if (el.value) {
|
|
1361
1386
|
formDataParams.append(el.dataset.pname, el.value);
|
|
1362
|
-
curlForm += ` -F "${el.dataset.pname}=${el.value}" \\\n`;
|
|
1363
1387
|
}
|
|
1364
1388
|
} else if (el.value && Array.isArray(el.value)) {
|
|
1365
|
-
el.value.forEach((v) => {
|
|
1366
|
-
curlForm = `${curlForm} -F "${el.dataset.pname}[]=${v}" \\\n`;
|
|
1367
|
-
});
|
|
1368
1389
|
formDataParams.append(el.dataset.pname, el.value.join(','));
|
|
1369
1390
|
}
|
|
1370
1391
|
});
|
|
@@ -1373,34 +1394,27 @@ export default class ApiRequest extends LitElement {
|
|
|
1373
1394
|
const bodyParamFileEl = requestPanelEl.querySelector('.request-body-param-file');
|
|
1374
1395
|
if (bodyParamFileEl?.files[0]) {
|
|
1375
1396
|
fetchOptions.body = bodyParamFileEl.files[0]; // eslint-disable-line prefer-destructuring
|
|
1376
|
-
curlData = ` --data-binary @${bodyParamFileEl.files[0].name} \\\n`;
|
|
1377
1397
|
}
|
|
1378
1398
|
} else if (requestBodyType.includes('json') || requestBodyType.includes('xml') || requestBodyType.includes('text')) {
|
|
1379
1399
|
const exampleTextAreaEl = requestPanelEl.querySelector('.request-body-param-user-input');
|
|
1380
1400
|
if (exampleTextAreaEl?.value) {
|
|
1381
1401
|
fetchOptions.body = exampleTextAreaEl.value;
|
|
1382
|
-
if (requestBodyType.includes('json')) {
|
|
1383
|
-
try {
|
|
1384
|
-
curlData = ` -d '${JSON.stringify(JSON.parse(exampleTextAreaEl.value))}' \\\n`;
|
|
1385
|
-
} catch (err) {
|
|
1386
|
-
// Ignore.
|
|
1387
|
-
}
|
|
1388
|
-
}
|
|
1389
|
-
if (!curlData) {
|
|
1390
|
-
curlData = ` -d '${exampleTextAreaEl.value.replace(/'/g, '\'"\'"\'')}' \\\n`;
|
|
1391
|
-
}
|
|
1392
1402
|
}
|
|
1393
1403
|
}
|
|
1394
|
-
// Common for all request-body
|
|
1395
|
-
if (!requestBodyType.includes('form-data')) {
|
|
1396
|
-
// For multipart/form-data dont set the content-type to allow creation of browser generated part boundaries
|
|
1397
|
-
reqHeaders.append('Content-Type', requestBodyType);
|
|
1398
|
-
}
|
|
1399
|
-
curlHeaders += ` -H "Content-Type: ${requestBodyType}" \\\n`;
|
|
1400
1404
|
}
|
|
1405
|
+
|
|
1406
|
+
return fetchOptions;
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
async onTryClick(e) {
|
|
1410
|
+
const tryBtnEl = e.target;
|
|
1411
|
+
const requestPanelEl = tryBtnEl.closest('.request-panel');
|
|
1412
|
+
const fetchUrl = this.buildFetchURL(requestPanelEl);
|
|
1413
|
+
const fetchOptions = this.buildFetchBodyOptions(requestPanelEl);
|
|
1414
|
+
const reqHeaders = this.buildFetchHeaders(requestPanelEl);
|
|
1401
1415
|
this.responseUrl = '';
|
|
1402
1416
|
this.responseHeaders = [];
|
|
1403
|
-
this.curlSyntax =
|
|
1417
|
+
this.curlSyntax = this.generateCURLSyntax(fetchUrl, reqHeaders, fetchOptions, requestPanelEl);
|
|
1404
1418
|
this.responseStatus = 'success';
|
|
1405
1419
|
this.responseIsBlob = false;
|
|
1406
1420
|
|
|
@@ -1409,22 +1423,28 @@ export default class ApiRequest extends LitElement {
|
|
|
1409
1423
|
URL.revokeObjectURL(this.responseBlobUrl);
|
|
1410
1424
|
this.responseBlobUrl = '';
|
|
1411
1425
|
}
|
|
1412
|
-
this.curlSyntax = `${curl}${curlHeaders}${curlData}${curlForm}`;
|
|
1413
1426
|
if (this.fetchCredentials) {
|
|
1414
1427
|
fetchOptions.credentials = this.fetchCredentials;
|
|
1415
1428
|
}
|
|
1416
1429
|
const controller = new AbortController();
|
|
1417
1430
|
const { signal } = controller;
|
|
1418
1431
|
fetchOptions.headers = reqHeaders;
|
|
1419
|
-
const
|
|
1432
|
+
const tempRequest = { url: fetchUrl, ...fetchOptions };
|
|
1420
1433
|
this.dispatchEvent(new CustomEvent('before-try', {
|
|
1421
1434
|
bubbles: true,
|
|
1422
1435
|
composed: true,
|
|
1423
1436
|
detail: {
|
|
1424
|
-
request:
|
|
1437
|
+
request: tempRequest,
|
|
1425
1438
|
controller,
|
|
1426
1439
|
},
|
|
1427
1440
|
}));
|
|
1441
|
+
const updatedFetchOptions = {
|
|
1442
|
+
method: tempRequest.method,
|
|
1443
|
+
headers: tempRequest.headers,
|
|
1444
|
+
credentials: tempRequest.credentials,
|
|
1445
|
+
body: tempRequest.body,
|
|
1446
|
+
};
|
|
1447
|
+
const fetchRequest = new Request(tempRequest.url, updatedFetchOptions);
|
|
1428
1448
|
|
|
1429
1449
|
let fetchResponse;
|
|
1430
1450
|
let responseClone;
|
|
@@ -1522,6 +1542,7 @@ export default class ApiRequest extends LitElement {
|
|
|
1522
1542
|
},
|
|
1523
1543
|
}));
|
|
1524
1544
|
this.responseMessage = 'Request Aborted';
|
|
1545
|
+
this.responseText = 'Request Aborted';
|
|
1525
1546
|
} else {
|
|
1526
1547
|
this.dispatchEvent(new CustomEvent('after-try', {
|
|
1527
1548
|
bubbles: true,
|
|
@@ -1537,6 +1558,90 @@ export default class ApiRequest extends LitElement {
|
|
|
1537
1558
|
this.requestUpdate();
|
|
1538
1559
|
}
|
|
1539
1560
|
|
|
1561
|
+
liveCURLSyntaxUpdate(requestPanelEl) {
|
|
1562
|
+
this.applyCURLSyntax(requestPanelEl);
|
|
1563
|
+
this.requestUpdate();
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
onGenerateCURLClick(e) {
|
|
1567
|
+
const requestPanelEl = this.getRequestPanel(e);
|
|
1568
|
+
this.applyCURLSyntax(requestPanelEl);
|
|
1569
|
+
}
|
|
1570
|
+
|
|
1571
|
+
getRequestPanel(e) {
|
|
1572
|
+
return e.target.closest('.request-panel');
|
|
1573
|
+
}
|
|
1574
|
+
|
|
1575
|
+
applyCURLSyntax(requestPanelEl) {
|
|
1576
|
+
const fetchUrl = this.buildFetchURL(requestPanelEl);
|
|
1577
|
+
const fetchOptions = this.buildFetchBodyOptions(requestPanelEl);
|
|
1578
|
+
const fetchHeaders = this.buildFetchHeaders(requestPanelEl);
|
|
1579
|
+
|
|
1580
|
+
this.curlSyntax = this.generateCURLSyntax(fetchUrl, fetchHeaders, fetchOptions, requestPanelEl);
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
generateCURLSyntax(fetchUrl, fetchHeaders, fetchOptions, requestPanelEl) {
|
|
1584
|
+
let curlUrl;
|
|
1585
|
+
let curl = '';
|
|
1586
|
+
let curlHeaders = '';
|
|
1587
|
+
let curlData = '';
|
|
1588
|
+
let curlForm = '';
|
|
1589
|
+
const requestBodyContainerEl = requestPanelEl.querySelector('.request-body-container');
|
|
1590
|
+
|
|
1591
|
+
if (fetchUrl.startsWith('http') === false) {
|
|
1592
|
+
const url = new URL(fetchUrl, window.location.href);
|
|
1593
|
+
curlUrl = url.href;
|
|
1594
|
+
} else {
|
|
1595
|
+
curlUrl = fetchUrl;
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
curl = `curl -X ${this.method.toUpperCase()} "${curlUrl}" \\\n`;
|
|
1599
|
+
|
|
1600
|
+
curlHeaders = Array.from(fetchHeaders).map(([key, value]) => ` -H "${key}: ${value}"`).join('\\\n');
|
|
1601
|
+
if (curlHeaders) {
|
|
1602
|
+
curlHeaders = `${curlHeaders} \\\n`;
|
|
1603
|
+
}
|
|
1604
|
+
if (fetchOptions.body instanceof URLSearchParams) {
|
|
1605
|
+
curlData = ` -d ${fetchOptions.body.toString()} \\\n`;
|
|
1606
|
+
} else if (fetchOptions.body instanceof File) {
|
|
1607
|
+
curlData = ` --data-binary @${fetchOptions.body.name} \\\n`;
|
|
1608
|
+
} else if (fetchOptions.body instanceof FormData) {
|
|
1609
|
+
curlForm = Array.from(fetchOptions.body).reduce((aggregator, [key, value]) => {
|
|
1610
|
+
if (value instanceof File) {
|
|
1611
|
+
return [...aggregator, ` -F "${key}=@${value.name}"`];
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
const multiple = value.match(/([^,],)/gm);
|
|
1615
|
+
|
|
1616
|
+
if (multiple) {
|
|
1617
|
+
const multipleResults = multiple.map((one) => `-F "${key}[]=${one}"`);
|
|
1618
|
+
|
|
1619
|
+
return [...aggregator, ...multipleResults];
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
return [...aggregator, ` -F "${key}=${value}"`];
|
|
1623
|
+
}, []).join('\\\n');
|
|
1624
|
+
} else if (requestBodyContainerEl && requestBodyContainerEl.dataset.selectedRequestBodyType) {
|
|
1625
|
+
const requestBodyType = requestBodyContainerEl.dataset.selectedRequestBodyType;
|
|
1626
|
+
const exampleTextAreaEl = requestPanelEl.querySelector('.request-body-param-user-input');
|
|
1627
|
+
if (exampleTextAreaEl?.value) {
|
|
1628
|
+
fetchOptions.body = exampleTextAreaEl.value;
|
|
1629
|
+
if (requestBodyType.includes('json')) {
|
|
1630
|
+
try {
|
|
1631
|
+
curlData = ` -d '${JSON.stringify(JSON.parse(exampleTextAreaEl.value))}' \\\n`;
|
|
1632
|
+
} catch (err) {
|
|
1633
|
+
// Ignore.
|
|
1634
|
+
}
|
|
1635
|
+
}
|
|
1636
|
+
if (!curlData) {
|
|
1637
|
+
curlData = ` -d '${exampleTextAreaEl.value.replace(/'/g, '\'"\'"\'')}' \\\n`;
|
|
1638
|
+
}
|
|
1639
|
+
}
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
return `${curl}${curlHeaders}${curlData}${curlForm}`;
|
|
1643
|
+
}
|
|
1644
|
+
|
|
1540
1645
|
onAddRemoveFileInput(e, pname, ptype) {
|
|
1541
1646
|
if (e.target.tagName.toLowerCase() !== 'button') {
|
|
1542
1647
|
return;
|
|
@@ -1592,7 +1697,8 @@ export default class ApiRequest extends LitElement {
|
|
|
1592
1697
|
}
|
|
1593
1698
|
|
|
1594
1699
|
disconnectedCallback() {
|
|
1595
|
-
|
|
1700
|
+
this.curlSyntax = '';
|
|
1701
|
+
// Cleanup ObjectURL for the blob data if this component created one
|
|
1596
1702
|
if (this.responseBlobUrl) {
|
|
1597
1703
|
URL.revokeObjectURL(this.responseBlobUrl);
|
|
1598
1704
|
this.responseBlobUrl = '';
|