rapidoc 9.3.2 → 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/index.html +1 -1
- package/dist/index.html.gz +0 -0
- package/dist/rapidoc-min.js +857 -738
- package/dist/rapidoc-min.js.LICENSE.txt +9 -10
- 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 +9341 -25092
- package/dist/report.html +5 -4
- package/index.html +2 -3
- package/package.json +20 -19
- package/src/components/api-request.js +344 -154
- package/src/components/api-response.js +16 -12
- package/src/components/json-tree.js +17 -12
- package/src/components/schema-table.js +50 -42
- package/src/components/schema-tree.js +56 -53
- package/src/components/tag-input.js +7 -4
- package/src/json-schema-viewer.js +1 -2
- package/src/rapidoc-mini.js +1 -3
- package/src/rapidoc.js +112 -45
- package/src/styles/endpoint-styles.js +1 -1
- package/src/styles/font-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 +6 -7
- package/src/templates/code-samples-template.js +1 -0
- package/src/templates/components-template.js +3 -5
- package/src/templates/endpoint-template.js +41 -27
- package/src/templates/expanded-endpoint-template.js +63 -13
- 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 +2 -5
- package/src/utils/schema-utils.js +208 -83
- package/src/utils/spec-parser.js +1 -0
- package/src/utils/theme.js +11 -5
- package/webpack.config.js +2 -1
|
@@ -3,6 +3,7 @@ import { unsafeHTML } from 'lit/directives/unsafe-html.js'; // eslint-disable-li
|
|
|
3
3
|
import { guard } from 'lit/directives/guard.js'; // eslint-disable-line import/extensions
|
|
4
4
|
import { live } from 'lit/directives/live.js'; // eslint-disable-line import/extensions
|
|
5
5
|
import { marked } from 'marked';
|
|
6
|
+
import formatXml from 'xml-but-prettier';
|
|
6
7
|
import Prism from 'prismjs';
|
|
7
8
|
import TableStyles from '~/styles/table-styles';
|
|
8
9
|
import FlexStyles from '~/styles/flex-styles';
|
|
@@ -12,8 +13,15 @@ import BorderStyles from '~/styles/border-styles';
|
|
|
12
13
|
import TabStyles from '~/styles/tab-styles';
|
|
13
14
|
import PrismStyles from '~/styles/prism-styles';
|
|
14
15
|
import CustomStyles from '~/styles/custom-styles';
|
|
15
|
-
import { copyToClipboard,
|
|
16
|
-
import { schemaInObjectNotation,
|
|
16
|
+
import { copyToClipboard, downloadResource, viewResource } from '~/utils/common-utils';
|
|
17
|
+
import { schemaInObjectNotation,
|
|
18
|
+
getTypeInfo,
|
|
19
|
+
generateExample,
|
|
20
|
+
normalizeExamples,
|
|
21
|
+
getSchemaFromParam,
|
|
22
|
+
json2xml,
|
|
23
|
+
nestExampleIfPresent,
|
|
24
|
+
anyExampleWithSummaryOrDescription } from '~/utils/schema-utils';
|
|
17
25
|
import '~/components/json-tree';
|
|
18
26
|
import '~/components/schema-tree';
|
|
19
27
|
import '~/components/tag-input';
|
|
@@ -46,14 +54,16 @@ export default class ApiRequest extends LitElement {
|
|
|
46
54
|
parser: { type: Object },
|
|
47
55
|
accept: { type: String },
|
|
48
56
|
callback: { type: String },
|
|
57
|
+
webhook: { type: String },
|
|
49
58
|
responseMessage: { type: String, attribute: false },
|
|
50
59
|
responseText: { type: String, attribute: false },
|
|
51
60
|
responseHeaders: { type: String, attribute: false },
|
|
52
61
|
responseStatus: { type: String, attribute: false },
|
|
53
62
|
responseUrl: { type: String, attribute: false },
|
|
63
|
+
curlSyntax: { type: String, attribute: false },
|
|
54
64
|
fillRequestFieldsWithExample: { type: String, attribute: 'fill-request-fields-with-example' },
|
|
55
|
-
useSummaryToListExamples: { type: String, attribute: 'use-summary-to-list-example' },
|
|
56
65
|
allowTry: { type: String, attribute: 'allow-try' },
|
|
66
|
+
showCurlBeforeTry: { type: String, attribute: 'show-curl-before-try' },
|
|
57
67
|
renderStyle: { type: String, attribute: 'render-style' },
|
|
58
68
|
schemaStyle: { type: String, attribute: 'schema-style' },
|
|
59
69
|
activeSchemaTab: { type: String, attribute: 'active-schema-tab' },
|
|
@@ -90,7 +100,9 @@ export default class ApiRequest extends LitElement {
|
|
|
90
100
|
PrismStyles,
|
|
91
101
|
css`
|
|
92
102
|
*, *:before, *:after { box-sizing: border-box; }
|
|
93
|
-
|
|
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;}
|
|
94
106
|
.read-mode {
|
|
95
107
|
margin-top: 24px;
|
|
96
108
|
}
|
|
@@ -194,70 +206,118 @@ export default class ApiRequest extends LitElement {
|
|
|
194
206
|
${this.callback === 'true' ? 'CALLBACK REQUEST' : 'REQUEST'}
|
|
195
207
|
</div>
|
|
196
208
|
<div>
|
|
197
|
-
${guard([this.allowTry, this.parameters, this.activeParameterSchemaTabs], () => this.inputParametersTemplate('path'))}
|
|
198
|
-
${guard([this.allowTry, this.parameters, this.activeParameterSchemaTabs], () => this.inputParametersTemplate('query'))}
|
|
209
|
+
${guard([this.method, this.path, this.allowTry, this.parameters, this.activeParameterSchemaTabs], () => this.inputParametersTemplate('path'))}
|
|
210
|
+
${guard([this.method, this.path, this.allowTry, this.parameters, this.activeParameterSchemaTabs], () => this.inputParametersTemplate('query'))}
|
|
199
211
|
${this.requestBodyTemplate()}
|
|
200
|
-
${guard([this.allowTry, this.parameters, this.activeParameterSchemaTabs], () => this.inputParametersTemplate('header'))}
|
|
201
|
-
${guard([this.allowTry, this.parameters, this.activeParameterSchemaTabs], () => this.inputParametersTemplate('cookie'))}
|
|
212
|
+
${guard([this.method, this.path, this.allowTry, this.parameters, this.activeParameterSchemaTabs], () => this.inputParametersTemplate('header'))}
|
|
213
|
+
${guard([this.method, this.path, this.allowTry, this.parameters, this.activeParameterSchemaTabs], () => this.inputParametersTemplate('cookie'))}
|
|
202
214
|
${this.allowTry === 'false' ? '' : html`${this.apiCallTemplate()}`}
|
|
203
215
|
</div>
|
|
204
216
|
</div>
|
|
205
217
|
`;
|
|
206
218
|
}
|
|
207
219
|
|
|
208
|
-
updated(
|
|
220
|
+
async updated() {
|
|
221
|
+
if (this.showCurlBeforeTry === 'true') {
|
|
222
|
+
this.applyCURLSyntax(this.shadowRoot);
|
|
223
|
+
}
|
|
224
|
+
|
|
209
225
|
// In focused mode after rendering the request component, update the text-areas(which contains examples) using
|
|
210
226
|
// the original values from hidden textareas
|
|
211
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
|
|
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';
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async saveExampleState() {
|
|
212
243
|
if (this.renderStyle === 'focused') {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
});
|
|
223
|
-
}
|
|
244
|
+
const reqBodyTextAreaEls = [...this.shadowRoot.querySelectorAll('textarea.request-body-param-user-input')];
|
|
245
|
+
reqBodyTextAreaEls.forEach((el) => {
|
|
246
|
+
el.dataset.user_example = el.value;
|
|
247
|
+
});
|
|
248
|
+
const exampleTextAreaEls = [...this.shadowRoot.querySelectorAll('textarea[data-ptype="form-data"]')];
|
|
249
|
+
exampleTextAreaEls.forEach((el) => {
|
|
250
|
+
el.dataset.user_example = el.value;
|
|
251
|
+
});
|
|
252
|
+
this.requestUpdate();
|
|
224
253
|
}
|
|
225
254
|
}
|
|
226
255
|
|
|
227
|
-
|
|
256
|
+
async updateExamplesFromDataAttr() {
|
|
257
|
+
// In focused mode after rendering the request component, update the text-areas(which contains examples) using
|
|
258
|
+
// the original values from hidden textareas
|
|
259
|
+
// 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
|
|
260
|
+
if (this.renderStyle === 'focused') {
|
|
261
|
+
const reqBodyTextAreaEls = [...this.shadowRoot.querySelectorAll('textarea.request-body-param-user-input')];
|
|
262
|
+
reqBodyTextAreaEls.forEach((el) => {
|
|
263
|
+
el.value = el.dataset.user_example || el.dataset.example;
|
|
264
|
+
});
|
|
265
|
+
const exampleTextAreaEls = [...this.shadowRoot.querySelectorAll('textarea[data-ptype="form-data"]')];
|
|
266
|
+
exampleTextAreaEls.forEach((el) => {
|
|
267
|
+
el.value = el.dataset.user_example || el.dataset.example;
|
|
268
|
+
});
|
|
269
|
+
this.requestUpdate();
|
|
270
|
+
}
|
|
271
|
+
}
|
|
228
272
|
|
|
229
|
-
|
|
273
|
+
/* eslint-disable indent */
|
|
274
|
+
renderExample(example, paramType, paramName) {
|
|
230
275
|
return html`
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
if (e.target.dataset.exampleType === 'array') {
|
|
243
|
-
inputEl.value = e.target.dataset.example.split('~|~');
|
|
244
|
-
} else {
|
|
245
|
-
inputEl.value = e.target.dataset.example;
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
}"
|
|
250
|
-
>
|
|
251
|
-
${this.useSummaryToListExamples === 'true'
|
|
252
|
-
? v.description || v.summary || (v.value && Array.isArray(v.value) ? (v.value?.join(', ')) : (v.value || ''))
|
|
253
|
-
: v.value && Array.isArray(v.value) ? (v.value?.join(', ')) : (v.value || '')
|
|
276
|
+
${paramType === 'array' ? '[' : ''}
|
|
277
|
+
<a
|
|
278
|
+
part="anchor anchor-param-example"
|
|
279
|
+
style="display:inline-block; min-width:24px; text-align:center"
|
|
280
|
+
class="${this.allowTry === 'true' ? '' : 'inactive-link'}"
|
|
281
|
+
data-example-type="${paramType === 'array' ? paramType : 'string'}"
|
|
282
|
+
data-example="${example.value && Array.isArray(example.value) ? example.value?.join('~|~') : example.value || ''}"
|
|
283
|
+
@click="${(e) => {
|
|
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;
|
|
254
287
|
}
|
|
255
|
-
|
|
256
|
-
|
|
288
|
+
}}"
|
|
289
|
+
> ${example.printableValue || example.value} </a>
|
|
290
|
+
${paramType === 'array' ? '] ' : ''}
|
|
291
|
+
`;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
renderShortFormatExamples(examples, paramType, paramName) {
|
|
295
|
+
return html`${examples.map((x, i) => html`
|
|
296
|
+
${i === 0 ? '' : '┃'}
|
|
297
|
+
${this.renderExample(x, paramType, paramName)}`)}`;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
renderLongFormatExamples(exampleList, paramType, paramName) {
|
|
301
|
+
return html` <ul style="list-style-type: disclosure-closed;">
|
|
302
|
+
${exampleList.map((v) => html`
|
|
303
|
+
<li>
|
|
304
|
+
${this.renderExample(v, paramType, paramName)}
|
|
305
|
+
${v.summary?.length > 0 ? html`<span>(${v.summary})</span>` : ''}
|
|
306
|
+
${v.description?.length > 0 ? html`<p>${unsafeHTML(marked(v.description))}</p>` : ''}
|
|
307
|
+
</li>
|
|
257
308
|
`)}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
309
|
+
</ul>`;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
exampleListTemplate(paramName, paramType, exampleList = []) {
|
|
313
|
+
return html` ${
|
|
314
|
+
exampleList.length > 0
|
|
315
|
+
? html`<span style="font-weight:bold">Examples: </span>
|
|
316
|
+
${anyExampleWithSummaryOrDescription(exampleList)
|
|
317
|
+
? this.renderLongFormatExamples(exampleList, paramType, paramName)
|
|
318
|
+
: this.renderShortFormatExamples(exampleList, paramType, paramName)}`
|
|
319
|
+
: ''
|
|
320
|
+
}`;
|
|
261
321
|
}
|
|
262
322
|
|
|
263
323
|
inputParametersTemplate(paramType) {
|
|
@@ -280,7 +340,7 @@ export default class ApiRequest extends LitElement {
|
|
|
280
340
|
for (const param of filteredParams) {
|
|
281
341
|
const [declaredParamSchema, serializeStyle, mimeTypeElem] = getSchemaFromParam(param);
|
|
282
342
|
if (!declaredParamSchema) {
|
|
283
|
-
continue;
|
|
343
|
+
continue;
|
|
284
344
|
}
|
|
285
345
|
const paramSchema = getTypeInfo(declaredParamSchema);
|
|
286
346
|
if (!paramSchema) {
|
|
@@ -305,14 +365,13 @@ export default class ApiRequest extends LitElement {
|
|
|
305
365
|
paramAllowReserved = param.allowReserved;
|
|
306
366
|
}
|
|
307
367
|
}
|
|
308
|
-
|
|
309
368
|
// openapi 3.1.0 spec based examples (which must be Object(string : { value:any, summary?: string, description?: string})
|
|
310
369
|
const example = normalizeExamples(
|
|
311
370
|
(param.examples
|
|
312
371
|
|| nestExampleIfPresent(param.example)
|
|
313
372
|
|| nestExampleIfPresent(mimeTypeElem?.example)
|
|
314
373
|
|| mimeTypeElem?.examples
|
|
315
|
-
|| paramSchema.examples
|
|
374
|
+
|| nestExampleIfPresent(paramSchema.examples)
|
|
316
375
|
|| nestExampleIfPresent(paramSchema.example)
|
|
317
376
|
),
|
|
318
377
|
paramSchema.type,
|
|
@@ -323,8 +382,9 @@ export default class ApiRequest extends LitElement {
|
|
|
323
382
|
serializeStyle || 'json',
|
|
324
383
|
'',
|
|
325
384
|
'',
|
|
326
|
-
|
|
327
|
-
|
|
385
|
+
this.callback === 'true' || this.webhook === 'true' ? true : false, // eslint-disable-line no-unneeded-ternary
|
|
386
|
+
this.callback === 'true' || this.webhook === 'true' ? false : true, // eslint-disable-line no-unneeded-ternary
|
|
387
|
+
true,
|
|
328
388
|
'text',
|
|
329
389
|
false,
|
|
330
390
|
)[0].exampleValue;
|
|
@@ -358,9 +418,13 @@ export default class ApiRequest extends LitElement {
|
|
|
358
418
|
data-param-serialize-style = "${paramStyle}"
|
|
359
419
|
data-param-serialize-explode = "${paramExplode}"
|
|
360
420
|
data-param-allow-reserved = "${paramAllowReserved}"
|
|
421
|
+
data-x-fill-example = "${param['x-fill-example'] || 'yes'}"
|
|
361
422
|
data-array = "true"
|
|
362
423
|
placeholder = "add-multiple ↩"
|
|
363
|
-
.value
|
|
424
|
+
.value="${param['x-fill-example'] === 'no'
|
|
425
|
+
? []
|
|
426
|
+
: live(this.fillRequestFieldsWithExample === 'true' ? Array.isArray(example.exampleVal) ? example.exampleVal : [example.exampleVal] : [])
|
|
427
|
+
}"
|
|
364
428
|
>
|
|
365
429
|
</tag-input>`
|
|
366
430
|
: paramSchema.type === 'object'
|
|
@@ -387,25 +451,30 @@ export default class ApiRequest extends LitElement {
|
|
|
387
451
|
data-param-serialize-style = "${paramStyle}"
|
|
388
452
|
data-param-serialize-explode = "${paramExplode}"
|
|
389
453
|
data-param-allow-reserved = "${paramAllowReserved}"
|
|
454
|
+
data-x-fill-example = "${param['x-fill-example'] || 'yes'}"
|
|
390
455
|
spellcheck = "false"
|
|
391
|
-
.textContent
|
|
456
|
+
.textContent="${param['x-fill-example'] === 'no' ? '' : live(this.fillRequestFieldsWithExample === 'true' ? example.exampleVal : '')}"
|
|
392
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
|
+
}}
|
|
393
462
|
></textarea>
|
|
394
463
|
</div>`
|
|
395
464
|
: html`
|
|
396
|
-
<div class="tab-content col">
|
|
465
|
+
<div class="tab-content col">
|
|
397
466
|
<schema-tree
|
|
398
467
|
class = 'json'
|
|
399
468
|
style = 'display: block'
|
|
400
469
|
.data = '${schemaAsObj}'
|
|
401
470
|
schema-expand-level = "${this.schemaExpandLevel}"
|
|
402
471
|
schema-description-expanded = "${this.schemaDescriptionExpanded}"
|
|
403
|
-
allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}"
|
|
472
|
+
allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}"
|
|
404
473
|
schema-hide-read-only = "${this.schemaHideReadOnly.includes(this.method)}"
|
|
405
474
|
schema-hide-write-only = "${this.schemaHideWriteOnly.includes(this.method)}"
|
|
406
|
-
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,
|
|
407
|
-
|
|
408
|
-
|
|
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"
|
|
409
478
|
> </schema-tree>
|
|
410
479
|
</div>`
|
|
411
480
|
}
|
|
@@ -421,6 +490,10 @@ export default class ApiRequest extends LitElement {
|
|
|
421
490
|
data-x-fill-example = "${param['x-fill-example'] || 'yes'}"
|
|
422
491
|
data-array="false"
|
|
423
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
|
+
}}
|
|
424
497
|
/>`
|
|
425
498
|
}
|
|
426
499
|
</td>`
|
|
@@ -469,15 +542,22 @@ export default class ApiRequest extends LitElement {
|
|
|
469
542
|
return html`
|
|
470
543
|
<div class="table-title top-gap">${title}</div>
|
|
471
544
|
<div style="display:block; overflow-x:auto; max-width:100%;">
|
|
472
|
-
<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;">
|
|
473
546
|
${tableRows}
|
|
474
547
|
</table>
|
|
475
548
|
</div>`;
|
|
476
549
|
}
|
|
477
550
|
|
|
478
|
-
|
|
551
|
+
// This method is called before navigation change in focused mode
|
|
552
|
+
async beforeNavigationFocusedMode() {
|
|
553
|
+
// this.saveExampleState();
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// This method is called after navigation change in focused mode
|
|
557
|
+
async afterNavigationFocusedMode() {
|
|
479
558
|
this.selectedRequestBodyType = '';
|
|
480
559
|
this.selectedRequestBodyExample = '';
|
|
560
|
+
this.updateExamplesFromDataAttr();
|
|
481
561
|
this.clearResponseData();
|
|
482
562
|
}
|
|
483
563
|
|
|
@@ -489,6 +569,9 @@ export default class ApiRequest extends LitElement {
|
|
|
489
569
|
const readOnlyExampleEl = selectEl.closest('.example-panel').querySelector('.request-body-param');
|
|
490
570
|
const userInputExampleTextareaEl = selectEl.closest('.example-panel').querySelector('.request-body-param-user-input');
|
|
491
571
|
userInputExampleTextareaEl.value = readOnlyExampleEl.innerText;
|
|
572
|
+
|
|
573
|
+
const requestPanelEl = this.getRequestPanel({ target: selectEl });
|
|
574
|
+
this.liveCURLSyntaxUpdate(requestPanelEl);
|
|
492
575
|
}, 0, exampleDropdownEl);
|
|
493
576
|
}
|
|
494
577
|
|
|
@@ -559,8 +642,8 @@ export default class ApiRequest extends LitElement {
|
|
|
559
642
|
reqBody.mimeType,
|
|
560
643
|
reqBody.examples,
|
|
561
644
|
reqBody.example,
|
|
562
|
-
|
|
563
|
-
|
|
645
|
+
this.callback === 'true' || this.webhook === 'true' ? true : false, // eslint-disable-line no-unneeded-ternary
|
|
646
|
+
this.callback === 'true' || this.webhook === 'true' ? false : true, // eslint-disable-line no-unneeded-ternary
|
|
564
647
|
'text',
|
|
565
648
|
false,
|
|
566
649
|
);
|
|
@@ -604,6 +687,10 @@ export default class ApiRequest extends LitElement {
|
|
|
604
687
|
data-example-format = "${v.exampleFormat}"
|
|
605
688
|
style="width:100%; resize:vertical;"
|
|
606
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
|
+
}}
|
|
607
694
|
></textarea>
|
|
608
695
|
</div>
|
|
609
696
|
`)}
|
|
@@ -618,8 +705,8 @@ export default class ApiRequest extends LitElement {
|
|
|
618
705
|
reqBody.mimeType,
|
|
619
706
|
reqBody.examples,
|
|
620
707
|
reqBody.example,
|
|
621
|
-
|
|
622
|
-
|
|
708
|
+
this.callback === 'true' || this.webhook === 'true' ? true : false, // eslint-disable-line no-unneeded-ternary
|
|
709
|
+
this.callback === 'true' || this.webhook === 'true' ? false : true, // eslint-disable-line no-unneeded-ternary
|
|
623
710
|
'text',
|
|
624
711
|
false,
|
|
625
712
|
);
|
|
@@ -649,7 +736,7 @@ export default class ApiRequest extends LitElement {
|
|
|
649
736
|
.data = '${schemaAsObj}'
|
|
650
737
|
schema-expand-level = "${this.schemaExpandLevel}"
|
|
651
738
|
schema-description-expanded = "${this.schemaDescriptionExpanded}"
|
|
652
|
-
allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}"
|
|
739
|
+
allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}"
|
|
653
740
|
schema-hide-read-only = "${this.schemaHideReadOnly}"
|
|
654
741
|
schema-hide-write-only = "${this.schemaHideWriteOnly}"
|
|
655
742
|
exportparts = "schema-description:schema-description, schema-multiline-toggle:schema-multiline-toggle"
|
|
@@ -664,7 +751,7 @@ export default class ApiRequest extends LitElement {
|
|
|
664
751
|
.data = "${schemaAsObj}"
|
|
665
752
|
schema-expand-level = "${this.schemaExpandLevel}"
|
|
666
753
|
schema-description-expanded = "${this.schemaDescriptionExpanded}"
|
|
667
|
-
allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}"
|
|
754
|
+
allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}"
|
|
668
755
|
schema-hide-read-only = "${this.schemaHideReadOnly}"
|
|
669
756
|
schema-hide-write-only = "${this.schemaHideWriteOnly}"
|
|
670
757
|
exportparts = "schema-description:schema-description, schema-multiline-toggle:schema-multiline-toggle"
|
|
@@ -691,10 +778,8 @@ export default class ApiRequest extends LitElement {
|
|
|
691
778
|
<button class="tab-btn ${this.activeSchemaTab === 'example' ? 'active' : ''}" data-tab = 'example'>EXAMPLE</button>
|
|
692
779
|
<button class="tab-btn ${this.activeSchemaTab !== 'example' ? 'active' : ''}" data-tab = 'schema'>SCHEMA</button>
|
|
693
780
|
</div>
|
|
694
|
-
${this.activeSchemaTab === 'example'
|
|
695
|
-
|
|
696
|
-
: html`<div class="tab-content col"> ${reqBodySchemaHtml}</div>`
|
|
697
|
-
}
|
|
781
|
+
${html`<div class="tab-content col" style="display:${this.activeSchemaTab === 'example' ? 'block' : 'none'};"> ${reqBodyExampleHtml}</div>`}
|
|
782
|
+
${html`<div class="tab-content col" style="display:${this.activeSchemaTab === 'example' ? 'none' : 'block'};"> ${reqBodySchemaHtml}</div>`}
|
|
698
783
|
</div>`
|
|
699
784
|
: html`
|
|
700
785
|
${reqBodyFileInputHtml}
|
|
@@ -712,8 +797,8 @@ export default class ApiRequest extends LitElement {
|
|
|
712
797
|
'json',
|
|
713
798
|
fieldSchema.examples,
|
|
714
799
|
fieldSchema.example,
|
|
715
|
-
|
|
716
|
-
|
|
800
|
+
this.callback === 'true' || this.webhook === 'true' ? true : false, // eslint-disable-line no-unneeded-ternary
|
|
801
|
+
this.callback === 'true' || this.webhook === 'true' ? false : true, // eslint-disable-line no-unneeded-ternary
|
|
717
802
|
'text',
|
|
718
803
|
false,
|
|
719
804
|
);
|
|
@@ -755,8 +840,6 @@ export default class ApiRequest extends LitElement {
|
|
|
755
840
|
.textContent = "${this.fillRequestFieldsWithExample === 'true' ? formdataPartExample[0].exampleValue : ''}"
|
|
756
841
|
spellcheck = "false"
|
|
757
842
|
></textarea>
|
|
758
|
-
<!-- This textarea(hidden) is to store the original example value, in focused mode on navbar change it is used to update the example text -->
|
|
759
|
-
<textarea data-pname = "hidden-${fieldName}" data-ptype = "${mimeType.includes('form-urlencode') ? 'hidden-form-urlencode' : 'hidden-form-data'}" class="is-hidden" style="display:none">${formdataPartExample[0].exampleValue}</textarea>
|
|
760
843
|
</div>`
|
|
761
844
|
}
|
|
762
845
|
${html`
|
|
@@ -901,7 +984,7 @@ export default class ApiRequest extends LitElement {
|
|
|
901
984
|
}`);
|
|
902
985
|
}
|
|
903
986
|
return html`
|
|
904
|
-
<table style="width:100%;" class="m-table">
|
|
987
|
+
<table role="presentation" style="width:100%;" class="m-table">
|
|
905
988
|
${formDataTableRows}
|
|
906
989
|
</table>
|
|
907
990
|
`;
|
|
@@ -921,8 +1004,34 @@ export default class ApiRequest extends LitElement {
|
|
|
921
1004
|
`;
|
|
922
1005
|
}
|
|
923
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
|
+
|
|
924
1016
|
apiResponseTabTemplate() {
|
|
925
|
-
|
|
1017
|
+
let responseFormat = '';
|
|
1018
|
+
let responseContent = '';
|
|
1019
|
+
if (!this.responseIsBlob) {
|
|
1020
|
+
if (this.responseHeaders.includes('application/x-ndjson')) {
|
|
1021
|
+
responseFormat = 'json';
|
|
1022
|
+
const prismLines = this.responseText.split('\n').map((q) => Prism.highlight(q, Prism.languages[responseFormat], responseFormat)).join('\n');
|
|
1023
|
+
responseContent = html`<code>${unsafeHTML(prismLines)}</code>`;
|
|
1024
|
+
} else if (this.responseHeaders.includes('json')) {
|
|
1025
|
+
responseFormat = 'json';
|
|
1026
|
+
responseContent = html`<code>${unsafeHTML(Prism.highlight(this.responseText, Prism.languages[responseFormat], responseFormat))}</code>`;
|
|
1027
|
+
} else if (this.responseHeaders.includes('html') || this.responseHeaders.includes('xml')) {
|
|
1028
|
+
responseFormat = 'html';
|
|
1029
|
+
responseContent = html`<code>${unsafeHTML(Prism.highlight(this.responseText, Prism.languages[responseFormat], responseFormat))}</code>`;
|
|
1030
|
+
} else {
|
|
1031
|
+
responseFormat = 'text';
|
|
1032
|
+
responseContent = html`<code>${this.responseText}</code>`;
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
926
1035
|
return html`
|
|
927
1036
|
<div class="row" style="font-size:var(--font-size-small); margin:5px 0">
|
|
928
1037
|
<div class="response-message ${this.responseStatus}">Response Status: ${this.responseMessage}</div>
|
|
@@ -936,7 +1045,9 @@ export default class ApiRequest extends LitElement {
|
|
|
936
1045
|
}}">
|
|
937
1046
|
<button class="tab-btn ${this.activeResponseTab === 'response' ? 'active' : ''}" data-tab = 'response' > RESPONSE</button>
|
|
938
1047
|
<button class="tab-btn ${this.activeResponseTab === 'headers' ? 'active' : ''}" data-tab = 'headers' > RESPONSE HEADERS</button>
|
|
939
|
-
|
|
1048
|
+
${this.showCurlBeforeTry === 'true'
|
|
1049
|
+
? ''
|
|
1050
|
+
: html`<button class="tab-btn ${this.activeResponseTab === 'curl' ? 'active' : ''}" data-tab = 'curl'>CURL</button>`}
|
|
940
1051
|
</div>
|
|
941
1052
|
${this.responseIsBlob
|
|
942
1053
|
? html`
|
|
@@ -952,20 +1063,14 @@ export default class ApiRequest extends LitElement {
|
|
|
952
1063
|
: html`
|
|
953
1064
|
<div class="tab-content col m-markdown" style="flex:1; display:${this.activeResponseTab === 'response' ? 'flex' : 'none'};" >
|
|
954
1065
|
<button class="toolbar-btn" style="position:absolute; top:12px; right:8px" @click='${(e) => { copyToClipboard(this.responseText, e); }}' part="btn btn-fill"> Copy </button>
|
|
955
|
-
<pre style="white-space:pre; min-height:50px; height:400px; resize:vertical; overflow:auto">${
|
|
956
|
-
? html`<code>${unsafeHTML(Prism.highlight(this.responseText, Prism.languages[responseFormat], responseFormat))}</code>`
|
|
957
|
-
: `${this.responseText}`
|
|
958
|
-
}</pre>
|
|
1066
|
+
<pre style="white-space:pre; min-height:50px; height:var(--resp-area-height, 400px); resize:vertical; overflow:auto">${responseContent}</pre>
|
|
959
1067
|
</div>`
|
|
960
1068
|
}
|
|
961
1069
|
<div class="tab-content col m-markdown" style="flex:1; display:${this.activeResponseTab === 'headers' ? 'flex' : 'none'};" >
|
|
962
1070
|
<button class="toolbar-btn" style = "position:absolute; top:12px; right:8px" @click='${(e) => { copyToClipboard(this.responseHeaders, e); }}' part="btn btn-fill"> Copy </button>
|
|
963
1071
|
<pre style="white-space:pre"><code>${unsafeHTML(Prism.highlight(this.responseHeaders, Prism.languages.css, 'css'))}</code></pre>
|
|
964
1072
|
</div>
|
|
965
|
-
|
|
966
|
-
<button class="toolbar-btn" style = "position:absolute; top:12px; right:8px" @click='${(e) => { copyToClipboard(this.curlSyntax.replace(/\\$/, ''), e); }}' part="btn btn-fill"> Copy </button>
|
|
967
|
-
<pre style="white-space:pre"><code>${unsafeHTML(Prism.highlight(this.curlSyntax.trim().replace(/\\$/, ''), Prism.languages.shell, 'shell'))}</code></pre>
|
|
968
|
-
</div>
|
|
1073
|
+
${this.showCurlBeforeTry === 'true' ? '' : this.curlSyntaxTemplate(this.activeResponseTab === 'curl' ? 'flex' : 'none')}
|
|
969
1074
|
</div>`;
|
|
970
1075
|
}
|
|
971
1076
|
|
|
@@ -995,7 +1100,7 @@ export default class ApiRequest extends LitElement {
|
|
|
995
1100
|
`;
|
|
996
1101
|
|
|
997
1102
|
return html`
|
|
998
|
-
<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">
|
|
999
1104
|
<div class="hide-in-small-screen" style="flex-direction:column; margin:0; width:calc(100% - 60px);">
|
|
1000
1105
|
<div style="display:flex; flex-direction:row; align-items:center; overflow:hidden;">
|
|
1001
1106
|
${selectedServerHtml}
|
|
@@ -1030,6 +1135,9 @@ export default class ApiRequest extends LitElement {
|
|
|
1030
1135
|
}
|
|
1031
1136
|
<button class="m-btn primary thin-border" part="btn btn-try" @click="${this.onTryClick}">TRY</button>
|
|
1032
1137
|
</div>
|
|
1138
|
+
<div class="row" style="font-size:var(--font-size-small); margin:5px 0">
|
|
1139
|
+
${this.showCurlBeforeTry === 'true' ? this.curlSyntaxTemplate() : ''}
|
|
1140
|
+
</div>
|
|
1033
1141
|
${this.responseMessage === '' ? '' : this.apiResponseTabTemplate()}
|
|
1034
1142
|
`;
|
|
1035
1143
|
}
|
|
@@ -1055,27 +1163,12 @@ export default class ApiRequest extends LitElement {
|
|
|
1055
1163
|
requestPanelInputEls.forEach((el) => { el.value = ''; });
|
|
1056
1164
|
}
|
|
1057
1165
|
|
|
1058
|
-
|
|
1059
|
-
// const me = this;
|
|
1060
|
-
const tryBtnEl = e.target;
|
|
1166
|
+
buildFetchURL(requestPanelEl) {
|
|
1061
1167
|
let fetchUrl;
|
|
1062
|
-
let curlUrl;
|
|
1063
|
-
let curl = '';
|
|
1064
|
-
let curlHeaders = '';
|
|
1065
|
-
let curlData = '';
|
|
1066
|
-
let curlForm = '';
|
|
1067
|
-
const respEl = this.closest('.expanded-req-resp-container, .req-resp-container')?.getElementsByTagName('api-response')[0];
|
|
1068
|
-
const acceptHeader = respEl?.selectedMimeType;
|
|
1069
|
-
const requestPanelEl = e.target.closest('.request-panel');
|
|
1070
1168
|
const pathParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='path']")];
|
|
1071
1169
|
const queryParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='query']")];
|
|
1072
1170
|
const queryParamObjTypeEls = [...requestPanelEl.querySelectorAll("[data-ptype='query-object']")];
|
|
1073
|
-
const headerParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='header']")];
|
|
1074
|
-
const requestBodyContainerEl = requestPanelEl.querySelector('.request-body-container');
|
|
1075
1171
|
fetchUrl = this.path;
|
|
1076
|
-
const fetchOptions = {
|
|
1077
|
-
method: this.method.toUpperCase(),
|
|
1078
|
-
};
|
|
1079
1172
|
// Generate URL using Path Params
|
|
1080
1173
|
pathParamEls.map((el) => {
|
|
1081
1174
|
fetchUrl = fetchUrl.replace(`{${el.dataset.pname}}`, encodeURIComponent(el.value));
|
|
@@ -1159,7 +1252,7 @@ export default class ApiRequest extends LitElement {
|
|
|
1159
1252
|
}
|
|
1160
1253
|
}
|
|
1161
1254
|
} catch (err) {
|
|
1162
|
-
console.
|
|
1255
|
+
console.error('RapiDoc: unable to parse %s into object', el.value); // eslint-disable-line no-console
|
|
1163
1256
|
}
|
|
1164
1257
|
if (queryParam.toString()) {
|
|
1165
1258
|
urlQueryParamsMap.set(el.dataset.pname, queryParam);
|
|
@@ -1168,7 +1261,6 @@ export default class ApiRequest extends LitElement {
|
|
|
1168
1261
|
}
|
|
1169
1262
|
let urlQueryParamString = '';
|
|
1170
1263
|
if (urlQueryParamsMap.size) {
|
|
1171
|
-
urlQueryParamString = '?';
|
|
1172
1264
|
urlQueryParamsMap.forEach((val, pname) => {
|
|
1173
1265
|
if (queryParamsWithReservedCharsAllowed.includes(pname)) {
|
|
1174
1266
|
urlQueryParamString += `${pname}=`;
|
|
@@ -1180,7 +1272,9 @@ export default class ApiRequest extends LitElement {
|
|
|
1180
1272
|
});
|
|
1181
1273
|
urlQueryParamString = urlQueryParamString.slice(0, -1);
|
|
1182
1274
|
}
|
|
1183
|
-
|
|
1275
|
+
if (urlQueryParamString.length !== 0) {
|
|
1276
|
+
fetchUrl = `${fetchUrl}${fetchUrl.includes('?') ? '&' : '?'}${urlQueryParamString}`;
|
|
1277
|
+
}
|
|
1184
1278
|
|
|
1185
1279
|
// Add authentication Query-Param if provided
|
|
1186
1280
|
this.api_keys
|
|
@@ -1189,23 +1283,21 @@ export default class ApiRequest extends LitElement {
|
|
|
1189
1283
|
fetchUrl = `${fetchUrl}${fetchUrl.includes('?') ? '&' : '?'}${v.name}=${encodeURIComponent(v.finalKeyValue)}`;
|
|
1190
1284
|
});
|
|
1191
1285
|
|
|
1192
|
-
// Final URL for API call
|
|
1193
1286
|
fetchUrl = `${this.serverUrl.replace(/\/$/, '')}${fetchUrl}`;
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
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;
|
|
1201
1295
|
const reqHeaders = new Headers();
|
|
1202
1296
|
if (acceptHeader) {
|
|
1203
1297
|
// Uses the acceptHeader from Response panel
|
|
1204
1298
|
reqHeaders.append('Accept', acceptHeader);
|
|
1205
|
-
curlHeaders += ` -H "Accept: ${acceptHeader}" \\\n`;
|
|
1206
1299
|
} else if (this.accept) {
|
|
1207
1300
|
reqHeaders.append('Accept', this.accept);
|
|
1208
|
-
curlHeaders += ` -H "Accept: ${this.accept}" \\\n`;
|
|
1209
1301
|
}
|
|
1210
1302
|
|
|
1211
1303
|
// Add Authentication Header if provided
|
|
@@ -1213,18 +1305,32 @@ export default class ApiRequest extends LitElement {
|
|
|
1213
1305
|
.filter((v) => (v.in === 'header'))
|
|
1214
1306
|
.forEach((v) => {
|
|
1215
1307
|
reqHeaders.append(v.name, v.finalKeyValue);
|
|
1216
|
-
curlHeaders += ` -H "${v.name}: ${v.finalKeyValue}" \\\n`;
|
|
1217
1308
|
});
|
|
1218
1309
|
|
|
1219
1310
|
// Add Header Params
|
|
1220
1311
|
headerParamEls.map((el) => {
|
|
1221
1312
|
if (el.value) {
|
|
1222
1313
|
reqHeaders.append(el.dataset.pname, el.value);
|
|
1223
|
-
curlHeaders += ` -H "${el.dataset.pname}: ${el.value}" \\\n`;
|
|
1224
1314
|
}
|
|
1225
1315
|
});
|
|
1226
1316
|
|
|
1227
|
-
|
|
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
|
+
};
|
|
1228
1334
|
if (requestBodyContainerEl) {
|
|
1229
1335
|
const requestBodyType = requestBodyContainerEl.dataset.selectedRequestBodyType;
|
|
1230
1336
|
if (requestBodyType.includes('form-urlencoded')) {
|
|
@@ -1250,7 +1356,6 @@ export default class ApiRequest extends LitElement {
|
|
|
1250
1356
|
formUrlDynParams.append(prop, JSON.stringify(tmpObj[prop]));
|
|
1251
1357
|
}
|
|
1252
1358
|
fetchOptions.body = formUrlDynParams;
|
|
1253
|
-
curlData = ` -d ${formUrlDynParams.toString()} \\\n`;
|
|
1254
1359
|
}
|
|
1255
1360
|
} else {
|
|
1256
1361
|
// url-encoded Form Params (regular)
|
|
@@ -1269,7 +1374,6 @@ export default class ApiRequest extends LitElement {
|
|
|
1269
1374
|
}
|
|
1270
1375
|
});
|
|
1271
1376
|
fetchOptions.body = formUrlParams;
|
|
1272
|
-
curlData = ` -d ${formUrlParams.toString()} \\\n`;
|
|
1273
1377
|
}
|
|
1274
1378
|
} else if (requestBodyType.includes('form-data')) {
|
|
1275
1379
|
const formDataParams = new FormData();
|
|
@@ -1278,15 +1382,10 @@ export default class ApiRequest extends LitElement {
|
|
|
1278
1382
|
if (el.dataset.array === 'false') {
|
|
1279
1383
|
if (el.type === 'file' && el.files[0]) {
|
|
1280
1384
|
formDataParams.append(el.dataset.pname, el.files[0], el.files[0].name);
|
|
1281
|
-
curlForm += ` -F "${el.dataset.pname}=@${el.files[0].name}" \\\n`;
|
|
1282
1385
|
} else if (el.value) {
|
|
1283
1386
|
formDataParams.append(el.dataset.pname, el.value);
|
|
1284
|
-
curlForm += ` -F "${el.dataset.pname}=${el.value}" \\\n`;
|
|
1285
1387
|
}
|
|
1286
1388
|
} else if (el.value && Array.isArray(el.value)) {
|
|
1287
|
-
el.value.forEach((v) => {
|
|
1288
|
-
curlForm = `${curlForm} -F "${el.dataset.pname}[]=${v}" \\\n`;
|
|
1289
|
-
});
|
|
1290
1389
|
formDataParams.append(el.dataset.pname, el.value.join(','));
|
|
1291
1390
|
}
|
|
1292
1391
|
});
|
|
@@ -1295,34 +1394,27 @@ export default class ApiRequest extends LitElement {
|
|
|
1295
1394
|
const bodyParamFileEl = requestPanelEl.querySelector('.request-body-param-file');
|
|
1296
1395
|
if (bodyParamFileEl?.files[0]) {
|
|
1297
1396
|
fetchOptions.body = bodyParamFileEl.files[0]; // eslint-disable-line prefer-destructuring
|
|
1298
|
-
curlData = ` --data-binary @${bodyParamFileEl.files[0].name} \\\n`;
|
|
1299
1397
|
}
|
|
1300
1398
|
} else if (requestBodyType.includes('json') || requestBodyType.includes('xml') || requestBodyType.includes('text')) {
|
|
1301
1399
|
const exampleTextAreaEl = requestPanelEl.querySelector('.request-body-param-user-input');
|
|
1302
1400
|
if (exampleTextAreaEl?.value) {
|
|
1303
1401
|
fetchOptions.body = exampleTextAreaEl.value;
|
|
1304
|
-
if (requestBodyType.includes('json')) {
|
|
1305
|
-
try {
|
|
1306
|
-
curlData = ` -d '${JSON.stringify(JSON.parse(exampleTextAreaEl.value))}' \\\n`;
|
|
1307
|
-
} catch (err) {
|
|
1308
|
-
// Ignore.
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
if (!curlData) {
|
|
1312
|
-
curlData = ` -d '${exampleTextAreaEl.value.replace(/'/g, '\'"\'"\'')}' \\\n`;
|
|
1313
|
-
}
|
|
1314
1402
|
}
|
|
1315
1403
|
}
|
|
1316
|
-
// Common for all request-body
|
|
1317
|
-
if (!requestBodyType.includes('form-data')) {
|
|
1318
|
-
// For multipart/form-data dont set the content-type to allow creation of browser generated part boundaries
|
|
1319
|
-
reqHeaders.append('Content-Type', requestBodyType);
|
|
1320
|
-
}
|
|
1321
|
-
curlHeaders += ` -H "Content-Type: ${requestBodyType}" \\\n`;
|
|
1322
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);
|
|
1323
1415
|
this.responseUrl = '';
|
|
1324
1416
|
this.responseHeaders = [];
|
|
1325
|
-
this.curlSyntax =
|
|
1417
|
+
this.curlSyntax = this.generateCURLSyntax(fetchUrl, reqHeaders, fetchOptions, requestPanelEl);
|
|
1326
1418
|
this.responseStatus = 'success';
|
|
1327
1419
|
this.responseIsBlob = false;
|
|
1328
1420
|
|
|
@@ -1331,22 +1423,28 @@ export default class ApiRequest extends LitElement {
|
|
|
1331
1423
|
URL.revokeObjectURL(this.responseBlobUrl);
|
|
1332
1424
|
this.responseBlobUrl = '';
|
|
1333
1425
|
}
|
|
1334
|
-
this.curlSyntax = `${curl}${curlHeaders}${curlData}${curlForm}`;
|
|
1335
1426
|
if (this.fetchCredentials) {
|
|
1336
1427
|
fetchOptions.credentials = this.fetchCredentials;
|
|
1337
1428
|
}
|
|
1338
1429
|
const controller = new AbortController();
|
|
1339
1430
|
const { signal } = controller;
|
|
1340
1431
|
fetchOptions.headers = reqHeaders;
|
|
1341
|
-
const
|
|
1432
|
+
const tempRequest = { url: fetchUrl, ...fetchOptions };
|
|
1342
1433
|
this.dispatchEvent(new CustomEvent('before-try', {
|
|
1343
1434
|
bubbles: true,
|
|
1344
1435
|
composed: true,
|
|
1345
1436
|
detail: {
|
|
1346
|
-
request:
|
|
1437
|
+
request: tempRequest,
|
|
1347
1438
|
controller,
|
|
1348
1439
|
},
|
|
1349
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);
|
|
1350
1448
|
|
|
1351
1449
|
let fetchResponse;
|
|
1352
1450
|
let responseClone;
|
|
@@ -1355,6 +1453,9 @@ export default class ApiRequest extends LitElement {
|
|
|
1355
1453
|
let respJson;
|
|
1356
1454
|
let respText;
|
|
1357
1455
|
tryBtnEl.disabled = true;
|
|
1456
|
+
this.responseText = '⌛';
|
|
1457
|
+
this.responseMessage = '';
|
|
1458
|
+
this.requestUpdate();
|
|
1358
1459
|
const startTime = performance.now();
|
|
1359
1460
|
fetchResponse = await fetch(fetchRequest, { signal });
|
|
1360
1461
|
const endTime = performance.now();
|
|
@@ -1372,7 +1473,9 @@ export default class ApiRequest extends LitElement {
|
|
|
1372
1473
|
if (respEmpty) {
|
|
1373
1474
|
this.responseText = '';
|
|
1374
1475
|
} else if (contentType) {
|
|
1375
|
-
if (contentType
|
|
1476
|
+
if (contentType === 'application/x-ndjson') {
|
|
1477
|
+
this.responseText = await fetchResponse.text();
|
|
1478
|
+
} else if (contentType.includes('json')) {
|
|
1376
1479
|
if ((/charset=[^"']+/).test(contentType)) {
|
|
1377
1480
|
const encoding = contentType.split('charset=')[1];
|
|
1378
1481
|
const buffer = await fetchResponse.arrayBuffer();
|
|
@@ -1401,9 +1504,10 @@ export default class ApiRequest extends LitElement {
|
|
|
1401
1504
|
} else {
|
|
1402
1505
|
respText = await fetchResponse.text();
|
|
1403
1506
|
if (contentType.includes('xml')) {
|
|
1404
|
-
this.responseText =
|
|
1507
|
+
this.responseText = formatXml(respText, { textNodesOnSameLine: true, indentor: ' ' });
|
|
1508
|
+
} else {
|
|
1509
|
+
this.responseText = respText;
|
|
1405
1510
|
}
|
|
1406
|
-
this.responseText = respText;
|
|
1407
1511
|
}
|
|
1408
1512
|
if (this.responseIsBlob) {
|
|
1409
1513
|
const contentDisposition = fetchResponse.headers.get('content-disposition');
|
|
@@ -1438,6 +1542,7 @@ export default class ApiRequest extends LitElement {
|
|
|
1438
1542
|
},
|
|
1439
1543
|
}));
|
|
1440
1544
|
this.responseMessage = 'Request Aborted';
|
|
1545
|
+
this.responseText = 'Request Aborted';
|
|
1441
1546
|
} else {
|
|
1442
1547
|
this.dispatchEvent(new CustomEvent('after-try', {
|
|
1443
1548
|
bubbles: true,
|
|
@@ -1453,6 +1558,90 @@ export default class ApiRequest extends LitElement {
|
|
|
1453
1558
|
this.requestUpdate();
|
|
1454
1559
|
}
|
|
1455
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
|
+
|
|
1456
1645
|
onAddRemoveFileInput(e, pname, ptype) {
|
|
1457
1646
|
if (e.target.tagName.toLowerCase() !== 'button') {
|
|
1458
1647
|
return;
|
|
@@ -1508,7 +1697,8 @@ export default class ApiRequest extends LitElement {
|
|
|
1508
1697
|
}
|
|
1509
1698
|
|
|
1510
1699
|
disconnectedCallback() {
|
|
1511
|
-
|
|
1700
|
+
this.curlSyntax = '';
|
|
1701
|
+
// Cleanup ObjectURL for the blob data if this component created one
|
|
1512
1702
|
if (this.responseBlobUrl) {
|
|
1513
1703
|
URL.revokeObjectURL(this.responseBlobUrl);
|
|
1514
1704
|
this.responseBlobUrl = '';
|