openapi-explorer 1.0.571 → 1.1.578
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/openapi-explorer.min.js +6 -6
- package/dist/browser/openapi-explorer.min.js.map +1 -1
- package/dist/es/components/api-request.js +98 -44
- package/dist/es/components/request-form-table.js +77 -11
- package/dist/es/utils/schema-utils.js +4 -1
- package/dist/lib/components/api-request.js +97 -43
- package/dist/lib/components/request-form-table.js +80 -11
- package/dist/lib/utils/schema-utils.js +5 -0
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@ import { keyed } from 'lit/directives/keyed.js';
|
|
|
7
7
|
import formatXml from 'xml-but-prettier';
|
|
8
8
|
import { copyToClipboard } from '../utils/common-utils';
|
|
9
9
|
import { getI18nText } from '../languages';
|
|
10
|
-
import { schemaInObjectNotation, getTypeInfo, generateExample } from '../utils/schema-utils';
|
|
10
|
+
import { schemaInObjectNotation, getTypeInfo, generateExample, isPatternProperty } from '../utils/schema-utils';
|
|
11
11
|
import './json-tree';
|
|
12
12
|
import './schema-tree';
|
|
13
13
|
import getRequestFormTable from './request-form-table';
|
|
@@ -21,6 +21,7 @@ export default class ApiRequest extends LitElement {
|
|
|
21
21
|
|
|
22
22
|
constructor() {
|
|
23
23
|
super();
|
|
24
|
+
this.duplicatedRowsByKey = {};
|
|
24
25
|
this.storedParamValues = {};
|
|
25
26
|
this.responseMessage = '';
|
|
26
27
|
this.responseStatus = '';
|
|
@@ -29,7 +30,7 @@ export default class ApiRequest extends LitElement {
|
|
|
29
30
|
this.responseUrl = '';
|
|
30
31
|
this.responseElapsedMs = 0;
|
|
31
32
|
this.curlSyntax = '';
|
|
32
|
-
this.activeResponseTab = '
|
|
33
|
+
this.activeResponseTab = 'curl'; // allowed values: response, headers, curl
|
|
33
34
|
|
|
34
35
|
this.selectedRequestBodyType = '';
|
|
35
36
|
this.selectedRequestBodyExample = '';
|
|
@@ -138,6 +139,10 @@ export default class ApiRequest extends LitElement {
|
|
|
138
139
|
attribute: 'fetch-credentials'
|
|
139
140
|
},
|
|
140
141
|
// properties for internal tracking
|
|
142
|
+
duplicatedRowsByKey: {
|
|
143
|
+
type: Object
|
|
144
|
+
},
|
|
145
|
+
// Tracking duplicated rows in form table
|
|
141
146
|
activeResponseTab: {
|
|
142
147
|
type: String
|
|
143
148
|
},
|
|
@@ -161,13 +166,8 @@ export default class ApiRequest extends LitElement {
|
|
|
161
166
|
}
|
|
162
167
|
|
|
163
168
|
updated(changedProperties) {
|
|
164
|
-
|
|
165
|
-
this.selectedRequestBodyType = '';
|
|
166
|
-
this.selectedRequestBodyExample = '';
|
|
167
|
-
} // In focused mode after rendering the request component, update the text-areas(which contains examples) using the original values from hidden textareas.
|
|
169
|
+
// In focused mode after rendering the request component, update the text-areas(which contains examples) using the original values from hidden textareas.
|
|
168
170
|
// 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
|
|
169
|
-
|
|
170
|
-
|
|
171
171
|
if (this.renderStyle !== 'focused') {
|
|
172
172
|
return;
|
|
173
173
|
} // dont update example as only tabs is switched
|
|
@@ -235,7 +235,10 @@ export default class ApiRequest extends LitElement {
|
|
|
235
235
|
|
|
236
236
|
tableRows.push(html` <tr> <td colspan="1" style="width:160px;min-width:50px;vertical-align:top"> <div class="param-name ${paramSchema.deprecated ? 'deprecated' : ''}" style="margin-top:1rem"> ${param.name}${!paramSchema.deprecated && param.required ? html`<span style="color:var(--red)">*</span>` : ''} </div> <div class="param-type" style="margin-bottom:1rem"> ${paramSchema.type === 'array' ? `${paramSchema.arrayType}` : `${paramSchema.format ? paramSchema.format : paramSchema.type}`}${!paramSchema.deprecated && param.required ? html`<span style="opacity:0">*</span>` : ''} </div> </td> <td colspan="2" style="min-width:160px;vertical-align:top"> ${this.allowTry === 'true' ? paramSchema.type === 'array' && html` <div style="margin-top:1rem;margin-bottom:1rem"> <tag-input class="request-param" style="width:100%" data-ptype="${paramLocation}" data-pname="${param.name}" data-default="${Array.isArray(defaultVal) ? defaultVal.join('~|~') : defaultVal}" data-param-serialize-style="${paramStyle}" data-param-serialize-explode="${paramExplode}" data-array="true" placeholder="add-multiple ↩" @change="${e => {
|
|
237
237
|
this.storedParamValues[param.name] = e.detail.value;
|
|
238
|
-
|
|
238
|
+
this.computeCurlSyntax();
|
|
239
|
+
}}" .value="${(_this$storedParamValu = this.storedParamValues[param.name]) !== null && _this$storedParamValu !== void 0 ? _this$storedParamValu : this.fillRequestWithDefault === 'true' && Array.isArray(defaultVal) ? defaultVal : defaultVal.split(',')}"></tag-input> </div>` || paramSchema.type === 'object' && html` <textarea class="textarea small request-param" part="textarea small textarea-param" rows="3" data-ptype="${paramLocation}" data-pname="${param.name}" data-default="${defaultVal}" data-param-serialize-style="${paramStyle}" data-param-serialize-explode="${paramExplode}" spellcheck="false" placeholder="${paramSchema.example || defaultVal || ''}" style="width:100%;margin-top:1rem;margin-bottom:1rem" .value="${this.fillRequestWithDefault === 'true' ? defaultVal : ''}"></textarea>` || html` <input type="${paramSchema.format === 'password' ? 'password' : 'text'}" spellcheck="false" style="width:100%;margin-top:1rem;margin-bottom:1rem" @input="${() => {
|
|
240
|
+
this.computeCurlSyntax();
|
|
241
|
+
}}" placeholder="${paramSchema.example || defaultVal || ''}" class="request-param" part="textbox textbox-param" data-ptype="${paramLocation}" data-pname="${param.name}" data-default="${Array.isArray(defaultVal) ? defaultVal.join('~|~') : defaultVal}" data-array="false" @keyup="${this.requestParamFunction}" .value="${this.fillRequestWithDefault === 'true' ? defaultVal : ''}">` : ''} ${this.exampleListTemplate.call(this, param, paramSchema.type)} </td> ${this.renderStyle === 'focused' ? html` <td colspan="2" style="vertical-align:top"> ${param.description ? html` <div class="param-description" style="margin-top:1rem"> ${unsafeHTML(marked(param.description))} </div>` : ''} ${paramSchema.default || paramSchema.s || paramSchema.allowedValues || paramSchema.pattern ? html` <div class="param-constraint" style="margin-top:1rem"> ${paramSchema.constraints.length ? html`<span style="font-weight:700">Constraints: </span>${paramSchema.constraints.join(', ')}<br>` : ''} ${paramSchema.pattern ? html` <div class="tooltip tooltip-replace" style="cursor:pointer;max-width:100%;display:flex"> <div style="white-space:nowrap;font-weight:700;margin-right:2px">Pattern: </div> <div style="white-space:nowrap;text-overflow:ellipsis;max-width:100%;overflow:hidden">${paramSchema.pattern}</div> <br> <div class="tooltip-text" style="position:absolute;display:block">${paramSchema.pattern}</div> </div> ` : ''} ${paramSchema.allowedValues && paramSchema.allowedValues.split('┃').map((v, i) => html` ${i > 0 ? '|' : html`<span style="font-weight:700">Allowed: </span>`} ${html` <a part="anchor anchor-param-constraint" class="${this.allowTry === 'true' ? '' : 'inactive-link'}" data-type="${paramSchema.type === 'array' ? 'array' : 'string'}" data-enum="${v.trim()}" @click="${e => {
|
|
239
242
|
const inputEl = e.target.closest('table').querySelector(`[data-pname="${param.name}"]`);
|
|
240
243
|
|
|
241
244
|
if (inputEl) {
|
|
@@ -308,6 +311,7 @@ export default class ApiRequest extends LitElement {
|
|
|
308
311
|
resetRequestBodySelection() {
|
|
309
312
|
this.selectedRequestBodyType = '';
|
|
310
313
|
this.selectedRequestBodyExample = '';
|
|
314
|
+
this.computeCurlSyntax();
|
|
311
315
|
this.clearResponseData();
|
|
312
316
|
} // Request-Body Event Handlers
|
|
313
317
|
|
|
@@ -319,6 +323,7 @@ export default class ApiRequest extends LitElement {
|
|
|
319
323
|
const exampleTextareaEl = selectEl.closest('.example-panel').querySelector('.request-body-param');
|
|
320
324
|
const userInputExampleTextareaEl = selectEl.closest('.example-panel').querySelector('.request-body-param-user-input');
|
|
321
325
|
userInputExampleTextareaEl.value = exampleTextareaEl.value;
|
|
326
|
+
this.computeCurlSyntax();
|
|
322
327
|
}, 0, exampleDropdownEl);
|
|
323
328
|
}
|
|
324
329
|
|
|
@@ -333,6 +338,8 @@ export default class ApiRequest extends LitElement {
|
|
|
333
338
|
const userInputExampleTextareaEl = selectEl.closest('.request-body-container').querySelector('.request-body-param-user-input');
|
|
334
339
|
userInputExampleTextareaEl.value = exampleTextareaEl.value;
|
|
335
340
|
}
|
|
341
|
+
|
|
342
|
+
this.computeCurlSyntax();
|
|
336
343
|
}, 0, mimeDropdownEl);
|
|
337
344
|
}
|
|
338
345
|
|
|
@@ -410,7 +417,7 @@ export default class ApiRequest extends LitElement {
|
|
|
410
417
|
}
|
|
411
418
|
}
|
|
412
419
|
|
|
413
|
-
return html` <div class="request-body-container" data-selected-request-body-type="${this.selectedRequestBodyType}"> <div class="table-title top-gap row">
|
|
420
|
+
return html` <div class="request-body-container" data-selected-request-body-type="${this.selectedRequestBodyType}"> <div class="table-title top-gap row"> ${getI18nText('operations.request-body')} ${this.request_body.required ? html`<span class="mono-font" style="color:var(--red)">*</span>` : ''} <span style="font-weight:400;margin-left:5px"> ${this.selectedRequestBodyType}</span> <span style="flex:1"></span> ${reqBodyTypeSelectorHtml} </div> ${this.request_body.description ? html`<div class="m-markdown" style="margin-bottom:12px">${unsafeHTML(marked(this.request_body.description))}</div>` : ''} ${reqBodySchemaHtml || reqBodyDefaultHtml ? html` <div class="tab-panel col" style="border-width:0 0 1px 0"> <div class="tab-buttons row" @click="${e => {
|
|
414
421
|
if (e.target.tagName.toLowerCase() === 'button') {
|
|
415
422
|
this.activeSchemaTab = e.target.dataset.tab;
|
|
416
423
|
}
|
|
@@ -431,14 +438,16 @@ export default class ApiRequest extends LitElement {
|
|
|
431
438
|
|
|
432
439
|
|
|
433
440
|
apiResponseTabTemplate() {
|
|
441
|
+
const curlSyntax = this.curlSyntax || this.computeCurlSyntax() || '';
|
|
442
|
+
const hasResponse = this.responseMessage !== '';
|
|
434
443
|
const responseFormat = this.responseHeaders.includes('json') ? 'json' : this.responseHeaders.includes('html') || this.responseHeaders.includes('xml') ? 'html' : '';
|
|
435
|
-
return html` <div class="row" style="font-size:var(--font-size-small);margin:5px 0"> ${this.responseMessage ? html`<div class="response-message ${this.responseStatus}">Response Status: ${this.responseMessage} ${this.responseElapsedMs ? html`<span><br>Execution Time: ${this.responseElapsedMs}ms</span>` : ''} </div>` : ''} <div style="flex:1"></div>
|
|
444
|
+
return html` <div class="row" style="font-size:var(--font-size-small);margin:5px 0"> ${this.responseMessage ? html`<div class="response-message ${this.responseStatus}">Response Status: ${this.responseMessage} ${this.responseElapsedMs ? html`<span><br>Execution Time: ${this.responseElapsedMs}ms</span>` : ''} </div>` : ''} <div style="flex:1"></div> ${!hasResponse ? '' : html`<button class="m-btn" part="btn btn-outline" @click="${this.clearResponseData}">CLEAR RESPONSE</button>`} </div> <div class="tab-panel col" style="border-width:0 0 1px 0"> <div id="tab_buttons" class="tab-buttons row" @click="${e => {
|
|
436
445
|
if (e.target.classList.contains('tab-btn') === false) {
|
|
437
446
|
return;
|
|
438
447
|
}
|
|
439
448
|
|
|
440
449
|
this.activeResponseTab = e.target.dataset.tab;
|
|
441
|
-
}}"> <button class="tab-btn ${this.activeResponseTab === 'response' ? 'active' : ''}" data-tab="response">${getI18nText('operations.response')}</button> <button class="tab-btn ${this.activeResponseTab === 'headers' ? 'active' : ''}" data-tab="headers">${getI18nText('operations.response-headers')}</button
|
|
450
|
+
}}"> <br> <div style="width:100%"> ${!hasResponse ? '' : html` <button class="tab-btn ${this.activeResponseTab === 'response' ? 'active' : ''}" data-tab="response">${getI18nText('operations.response')}</button> <button class="tab-btn ${this.activeResponseTab === 'headers' ? 'active' : ''}" data-tab="headers">${getI18nText('operations.response-headers')}</button>`} <button class="tab-btn ${!hasResponse || this.activeResponseTab === 'curl' ? 'active' : ''}" data-tab="curl">FULL REQUEST</button> </div> </div> ${this.responseIsBlob ? html` <div class="tab-content col" style="flex:1;display:${this.activeResponseTab === 'response' ? 'flex' : 'none'}"> <button class="m-btn thin-border mar-top-8" style="width:135px" @click="${this.downloadResponseBlob}" part="btn btn-outline">DOWNLOAD</button> ${this.responseBlobType === 'view' ? html`<button class="m-btn thin-border mar-top-8" style="width:135px" @click="${this.viewResponseBlob}" part="btn btn-outline">VIEW (NEW TAB)</button>` : ''} </div>` : html` <div class="tab-content col m-markdown" style="flex:1;display:${this.activeResponseTab === 'response' ? 'flex' : 'none'}"> ${this.responseText ? html`<button class="m-btn outline-primary toolbar-copy-btn" @click="${e => {
|
|
442
451
|
copyToClipboard(this.responseText, e);
|
|
443
452
|
}}" part="btn btn-fill">${getI18nText('operations.copy')}</button>` : ''} <pre style="min-height:60px" @copy="${() => {
|
|
444
453
|
copyToClipboard(window.getSelection().toString());
|
|
@@ -446,14 +455,14 @@ export default class ApiRequest extends LitElement {
|
|
|
446
455
|
</pre> </div>`} <div class="tab-content col m-markdown" style="flex:1;display:${this.activeResponseTab === 'headers' ? 'flex' : 'none'}"> <button class="m-btn outline-primary toolbar-copy-btn" @click="${e => {
|
|
447
456
|
copyToClipboard(this.responseHeaders, e);
|
|
448
457
|
}}" part="btn btn-fill">${getI18nText('operations.copy')}</button> <pre><code>${unsafeHTML(Prism.highlight(this.responseHeaders, Prism.languages.css, 'css'))}</code></pre> </div> <div class="tab-content col m-markdown" style="flex:1;display:${this.activeResponseTab === 'curl' ? 'flex' : 'none'}"> <button class="m-btn outline-primary toolbar-copy-btn" @click="${e => {
|
|
449
|
-
copyToClipboard(
|
|
458
|
+
copyToClipboard(curlSyntax, e);
|
|
450
459
|
}}" part="btn btn-fill">${getI18nText('operations.copy')}</button> <pre class="fs-exclude" data-hj-suppress data-sl="mask">
|
|
451
|
-
<code>${unsafeHTML(Prism.highlight(
|
|
460
|
+
<code>${unsafeHTML(Prism.highlight(curlSyntax.trim(), Prism.languages.shell, 'shell'))}</code>
|
|
452
461
|
</pre> </div> </div>`;
|
|
453
462
|
}
|
|
454
463
|
|
|
455
464
|
apiCallTemplate() {
|
|
456
|
-
return html` <div style="display:flex;align-items:flex-end;margin:16px 0;font-size:var(--font-size-small)"> ${this.parameters.length > 0 || this.request_body ? html` <button class="m-btn thin-border" part="btn btn-outline" style="margin-right:5px" @click="${this.onClearRequestData}"> ${getI18nText('operations.clear')} </button>` : ''} <button class="m-btn primary btn-execute thin-border" part="btn btn-fill btn-try" @click="${this.onTryClick}">${getI18nText('operations.execute')}</button> </div> ${this.
|
|
465
|
+
return html` <div style="display:flex;align-items:flex-end;margin:16px 0;font-size:var(--font-size-small)"> ${this.parameters.length > 0 || this.request_body ? html` <button class="m-btn thin-border" part="btn btn-outline" style="margin-right:5px" @click="${this.onClearRequestData}"> ${getI18nText('operations.clear')} </button>` : ''} <button class="m-btn primary btn-execute thin-border" part="btn btn-fill btn-try" @click="${this.onTryClick}">${getI18nText('operations.execute')}</button> </div> ${this.apiResponseTabTemplate()} `;
|
|
457
466
|
}
|
|
458
467
|
/* eslint-enable indent */
|
|
459
468
|
|
|
@@ -477,12 +486,10 @@ export default class ApiRequest extends LitElement {
|
|
|
477
486
|
}
|
|
478
487
|
};
|
|
479
488
|
this.dispatchEvent(new CustomEvent('event', event));
|
|
489
|
+
this.computeCurlSyntax();
|
|
480
490
|
}
|
|
481
491
|
|
|
482
|
-
|
|
483
|
-
const tryBtnEl = this.querySelectorAll('.btn-execute')[0];
|
|
484
|
-
let curlData = '';
|
|
485
|
-
let curlForm = '';
|
|
492
|
+
recomputeFetchOptions() {
|
|
486
493
|
const closestRespContainer = this.closest('.expanded-req-resp-container, .req-resp-container');
|
|
487
494
|
const respEl = closestRespContainer && closestRespContainer.getElementsByTagName('api-response')[0];
|
|
488
495
|
const acceptHeader = respEl === null || respEl === void 0 ? void 0 : respEl.selectedMimeType;
|
|
@@ -602,14 +609,21 @@ export default class ApiRequest extends LitElement {
|
|
|
602
609
|
fetchOptions.headers.append(el.dataset.pname, el.value);
|
|
603
610
|
}
|
|
604
611
|
}); // Request Body Params
|
|
612
|
+
// url-encoded Form Params (dynamic) - Parse JSON and generate Params
|
|
613
|
+
|
|
614
|
+
const formUrlDynamicTextAreaEl = requestPanelEl.querySelector("[data-ptype='dynamic-form']"); // url-encoded Form Params (regular)
|
|
615
|
+
|
|
616
|
+
const rawFormInputEls = [...requestPanelEl.querySelectorAll("[data-ptype='form-input']")];
|
|
617
|
+
const patternPropertyKeyEls = [...requestPanelEl.querySelectorAll("[data-ptype='pattern-property-key']")];
|
|
618
|
+
const patternPropertyInputEls = rawFormInputEls.filter(el => isPatternProperty(el.dataset.pname));
|
|
619
|
+
const formInputEls = rawFormInputEls.filter(el => !isPatternProperty(el.dataset.pname));
|
|
620
|
+
let curlData = '';
|
|
621
|
+
let curlForm = '';
|
|
605
622
|
|
|
606
623
|
if (requestBodyContainerEl) {
|
|
607
624
|
const requestBodyType = requestBodyContainerEl.dataset.selectedRequestBodyType;
|
|
608
625
|
|
|
609
626
|
if (requestBodyType.includes('form-urlencoded')) {
|
|
610
|
-
// url-encoded Form Params (dynamic) - Parse JSON and generate Params
|
|
611
|
-
const formUrlDynamicTextAreaEl = requestPanelEl.querySelector("[data-ptype='dynamic-form']");
|
|
612
|
-
|
|
613
627
|
if (formUrlDynamicTextAreaEl) {
|
|
614
628
|
const val = formUrlDynamicTextAreaEl.value;
|
|
615
629
|
const formUrlDynParams = new URLSearchParams();
|
|
@@ -636,17 +650,23 @@ export default class ApiRequest extends LitElement {
|
|
|
636
650
|
curlData = ` \\\n -d ${formUrlDynParams.toString()}`;
|
|
637
651
|
}
|
|
638
652
|
} else {
|
|
639
|
-
// url-encoded Form Params (regular)
|
|
640
|
-
const formUrlEls = [...requestPanelEl.querySelectorAll("[data-ptype='form-urlencode']")];
|
|
641
653
|
const formUrlParams = new URLSearchParams();
|
|
642
|
-
|
|
654
|
+
patternPropertyInputEls.concat(formInputEls).forEach((el, counter) => {
|
|
655
|
+
var _patternPropertyKeyEl;
|
|
656
|
+
|
|
657
|
+
const keyName = ((_patternPropertyKeyEl = patternPropertyKeyEls[counter]) === null || _patternPropertyKeyEl === void 0 ? void 0 : _patternPropertyKeyEl.value) || el.dataset.pname;
|
|
658
|
+
|
|
659
|
+
if (el.type === 'file') {
|
|
660
|
+
return;
|
|
661
|
+
}
|
|
662
|
+
|
|
643
663
|
if (el.dataset.array === 'false') {
|
|
644
664
|
if (el.value) {
|
|
645
|
-
formUrlParams.append(
|
|
665
|
+
formUrlParams.append(keyName, el.value);
|
|
646
666
|
}
|
|
647
667
|
} else {
|
|
648
668
|
const vals = el.value && Array.isArray(el.value) ? el.value.join(',') : '';
|
|
649
|
-
formUrlParams.append(
|
|
669
|
+
formUrlParams.append(keyName, vals);
|
|
650
670
|
}
|
|
651
671
|
});
|
|
652
672
|
fetchOptions.body = formUrlParams;
|
|
@@ -654,21 +674,24 @@ export default class ApiRequest extends LitElement {
|
|
|
654
674
|
}
|
|
655
675
|
} else if (requestBodyType.includes('form-data')) {
|
|
656
676
|
const formDataParams = new FormData();
|
|
657
|
-
|
|
658
|
-
|
|
677
|
+
patternPropertyInputEls.concat(formInputEls).forEach((el, counter) => {
|
|
678
|
+
var _patternPropertyKeyEl2;
|
|
679
|
+
|
|
680
|
+
const keyName = ((_patternPropertyKeyEl2 = patternPropertyKeyEls[counter]) === null || _patternPropertyKeyEl2 === void 0 ? void 0 : _patternPropertyKeyEl2.value) || el.dataset.pname;
|
|
681
|
+
|
|
659
682
|
if (el.dataset.array === 'false') {
|
|
660
683
|
if (el.type === 'file' && el.files[0]) {
|
|
661
|
-
formDataParams.append(
|
|
662
|
-
curlForm += ` \\\n -F "${
|
|
684
|
+
formDataParams.append(keyName, el.files[0], el.files[0].name);
|
|
685
|
+
curlForm += ` \\\n -F "${keyName}=@${el.files[0].name}"`;
|
|
663
686
|
} else if (el.value) {
|
|
664
|
-
formDataParams.append(
|
|
665
|
-
curlForm += ` \\\n -F "${
|
|
687
|
+
formDataParams.append(keyName, el.value);
|
|
688
|
+
curlForm += ` \\\n -F "${keyName}=${el.value}"`;
|
|
666
689
|
}
|
|
667
690
|
} else if (el.value && Array.isArray(el.value)) {
|
|
668
691
|
el.value.forEach(v => {
|
|
669
|
-
curlForm += ` \\\n -F "${
|
|
692
|
+
curlForm += ` \\\n -F "${keyName}[]=${v}"`;
|
|
670
693
|
});
|
|
671
|
-
formDataParams.append(
|
|
694
|
+
formDataParams.append(keyName, el.value.join(','));
|
|
672
695
|
}
|
|
673
696
|
});
|
|
674
697
|
fetchOptions.body = formDataParams;
|
|
@@ -707,16 +730,46 @@ export default class ApiRequest extends LitElement {
|
|
|
707
730
|
}
|
|
708
731
|
}
|
|
709
732
|
|
|
733
|
+
if (this.fetchCredentials) {
|
|
734
|
+
fetchOptions.credentials = this.fetchCredentials;
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
return {
|
|
738
|
+
fetchOptions,
|
|
739
|
+
fetchUrl,
|
|
740
|
+
curlParts: {
|
|
741
|
+
data: curlData,
|
|
742
|
+
form: curlForm
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
computeCurlSyntax(headerOverride) {
|
|
748
|
+
const {
|
|
749
|
+
fetchOptions,
|
|
750
|
+
fetchUrl,
|
|
751
|
+
curlParts
|
|
752
|
+
} = this.recomputeFetchOptions();
|
|
753
|
+
const curl = `curl -X ${this.method.toUpperCase()} "${fetchUrl.toString()}"`;
|
|
754
|
+
const headers = headerOverride !== null && headerOverride !== void 0 ? headerOverride : fetchOptions.headers;
|
|
755
|
+
const curlHeaders = [...headers.entries()].reduce((acc, [key, value]) => `${acc} \\\n -H "${key}: ${value}"`, '');
|
|
756
|
+
this.curlSyntax = `${curl}${curlHeaders}${curlParts.data}${curlParts.form}`;
|
|
757
|
+
this.requestUpdate();
|
|
758
|
+
} // onExecuteButtonClicked
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
async onTryClick() {
|
|
762
|
+
const tryBtnEl = this.querySelectorAll('.btn-execute')[0];
|
|
763
|
+
const {
|
|
764
|
+
fetchOptions,
|
|
765
|
+
fetchUrl
|
|
766
|
+
} = this.recomputeFetchOptions();
|
|
710
767
|
this.responseIsBlob = false;
|
|
711
768
|
this.respContentDisposition = '';
|
|
712
769
|
|
|
713
770
|
if (this.responseBlobUrl) {
|
|
714
771
|
URL.revokeObjectURL(this.responseBlobUrl);
|
|
715
772
|
this.responseBlobUrl = '';
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
if (this.fetchCredentials) {
|
|
719
|
-
fetchOptions.credentials = this.fetchCredentials;
|
|
720
773
|
} // Options is legacy usage, documentation has been updated to reference properties of the fetch option directly, but older usages may still be using options
|
|
721
774
|
|
|
722
775
|
|
|
@@ -742,9 +795,7 @@ export default class ApiRequest extends LitElement {
|
|
|
742
795
|
body: fetchRequest.body || fetchOptions.body
|
|
743
796
|
};
|
|
744
797
|
const fetchRequestObject = new Request(fetchRequest.url, newFetchOptions);
|
|
745
|
-
|
|
746
|
-
const curlHeaders = [...newFetchOptions.headers.entries()].reduce((acc, [key, value]) => `${acc} \\\n -H "${key}: ${value}"`, '');
|
|
747
|
-
this.curlSyntax = `${curl}${curlHeaders}${curlData}${curlForm}`;
|
|
798
|
+
this.computeCurlSyntax(newFetchOptions.headers);
|
|
748
799
|
let fetchResponse;
|
|
749
800
|
|
|
750
801
|
try {
|
|
@@ -758,6 +809,7 @@ export default class ApiRequest extends LitElement {
|
|
|
758
809
|
this.responseUrl = '';
|
|
759
810
|
this.responseHeaders = '';
|
|
760
811
|
this.responseText = '⌛';
|
|
812
|
+
this.activeResponseTab = 'response';
|
|
761
813
|
this.requestUpdate();
|
|
762
814
|
const awaiter = new Promise(resolve => setTimeout(resolve, 200));
|
|
763
815
|
fetchResponse = await fetch(fetchRequestObject);
|
|
@@ -864,7 +916,7 @@ export default class ApiRequest extends LitElement {
|
|
|
864
916
|
}
|
|
865
917
|
}
|
|
866
918
|
|
|
867
|
-
onAddRemoveFileInput(e, pname
|
|
919
|
+
onAddRemoveFileInput(e, pname) {
|
|
868
920
|
if (e.target.tagName.toLowerCase() !== 'button') {
|
|
869
921
|
return;
|
|
870
922
|
}
|
|
@@ -886,7 +938,7 @@ export default class ApiRequest extends LitElement {
|
|
|
886
938
|
newInputEl.type = 'file';
|
|
887
939
|
newInputEl.setAttribute('class', 'file-input');
|
|
888
940
|
newInputEl.setAttribute('data-pname', pname);
|
|
889
|
-
newInputEl.setAttribute('data-ptype',
|
|
941
|
+
newInputEl.setAttribute('data-ptype', 'form-input');
|
|
890
942
|
newInputEl.setAttribute('data-array', 'false');
|
|
891
943
|
newInputEl.setAttribute('data-file-array', 'true'); // Remover Button
|
|
892
944
|
|
|
@@ -896,6 +948,8 @@ export default class ApiRequest extends LitElement {
|
|
|
896
948
|
newInputContainerEl.appendChild(newInputEl);
|
|
897
949
|
newInputContainerEl.appendChild(newRemoveBtnEl);
|
|
898
950
|
el.insertBefore(newInputContainerEl, e.target); // el.appendChild(newInputContainerEl);
|
|
951
|
+
|
|
952
|
+
this.computeCurlSyntax();
|
|
899
953
|
}
|
|
900
954
|
|
|
901
955
|
downloadResponseBlob() {
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
import { html } from 'lit';
|
|
3
3
|
import { marked } from 'marked';
|
|
4
4
|
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
|
|
5
|
+
import { isPatternProperty } from '../utils/schema-utils';
|
|
6
|
+
import { map } from 'lit/directives/map.js';
|
|
7
|
+
import { range } from 'lit/directives/range.js';
|
|
5
8
|
|
|
6
9
|
function generateFormRows(data, options, dataType = 'object', key = '', description = '', schemaLevel = 0) {
|
|
7
10
|
const newSchemaLevel = data['::type'] && data['::type'].startsWith('xxx-of') ? schemaLevel : schemaLevel + 1;
|
|
@@ -45,6 +48,20 @@ function generateFormRows(data, options, dataType = 'object', key = '', descript
|
|
|
45
48
|
|
|
46
49
|
|
|
47
50
|
const parsedData = JSON.parse(data);
|
|
51
|
+
return generatePrimitiveRow.call(this, parsedData, {
|
|
52
|
+
key,
|
|
53
|
+
keyLabel,
|
|
54
|
+
keyDescr,
|
|
55
|
+
description,
|
|
56
|
+
dataType,
|
|
57
|
+
isRequired,
|
|
58
|
+
options
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function generatePrimitiveRow(rowData, parentRecursionOptions) {
|
|
63
|
+
var _this$duplicatedRowsB;
|
|
64
|
+
|
|
48
65
|
const {
|
|
49
66
|
type,
|
|
50
67
|
format,
|
|
@@ -57,13 +74,46 @@ function generateFormRows(data, options, dataType = 'object', key = '', descript
|
|
|
57
74
|
schemaDescription,
|
|
58
75
|
schemaTitle,
|
|
59
76
|
deprecated
|
|
60
|
-
} =
|
|
77
|
+
} = rowData;
|
|
78
|
+
const {
|
|
79
|
+
key,
|
|
80
|
+
keyLabel,
|
|
81
|
+
keyDescr,
|
|
82
|
+
description,
|
|
83
|
+
dataType,
|
|
84
|
+
isRequired,
|
|
85
|
+
options
|
|
86
|
+
} = parentRecursionOptions;
|
|
61
87
|
|
|
62
88
|
if (readOrWriteOnly === '🆁') {
|
|
63
89
|
return undefined;
|
|
64
90
|
}
|
|
65
91
|
|
|
66
|
-
|
|
92
|
+
const elementId = this.elementId || `${this.method}-${this.path}`;
|
|
93
|
+
const duplicateRowGeneratorKey = `${elementId}-${key}`;
|
|
94
|
+
|
|
95
|
+
const rowGenerator = e => {
|
|
96
|
+
var _e$target$dataset, _e$target$dataset2;
|
|
97
|
+
|
|
98
|
+
if (((_e$target$dataset = e.target.dataset) === null || _e$target$dataset === void 0 ? void 0 : _e$target$dataset.ptype) !== 'pattern-property-key' && !isPatternProperty((_e$target$dataset2 = e.target.dataset) === null || _e$target$dataset2 === void 0 ? void 0 : _e$target$dataset2.pname)) {
|
|
99
|
+
return;
|
|
100
|
+
} // If the row key has a value then add another row
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
const patternPropertyKeyEls = [...this.querySelectorAll("[data-ptype='pattern-property-key']")];
|
|
104
|
+
const patternPropertyInputEls = [...this.querySelectorAll("[data-ptype='form-input']")].filter(el => isPatternProperty(el.dataset.pname)); // If there is still some row that either has an empty key or an empty value, then skip adding a new row
|
|
105
|
+
|
|
106
|
+
if (patternPropertyKeyEls.some((keyElement, index) => !keyElement.value || !patternPropertyInputEls[index].value)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (e.target.value) {
|
|
111
|
+
this.duplicatedRowsByKey[duplicateRowGeneratorKey] = (this.duplicatedRowsByKey[duplicateRowGeneratorKey] || 1) + 1;
|
|
112
|
+
this.requestUpdate();
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
return map(range(((_this$duplicatedRowsB = this.duplicatedRowsByKey) === null || _this$duplicatedRowsB === void 0 ? void 0 : _this$duplicatedRowsB[duplicateRowGeneratorKey]) || 1), () => html` <tr> ${inputFieldKeyLabel.call(this, key.startsWith('::OPTION'), keyLabel, keyDescr, dataType, deprecated, isRequired, schemaTitle, format || type, rowGenerator)} ${dataType === 'array' ? getArrayFormField.call(this, keyLabel, example, defaultValue, format, rowGenerator) : ''} ${dataType !== 'array' ? getPrimitiveFormField.call(this, keyLabel, example, defaultValue, format, options, rowGenerator) : ''} <td> ${description ? html`<div class="param-description">${unsafeHTML(marked(description))}</div>` : ''} ${defaultValue || constraints || allowedValues || pattern ? html` <div class="param-constraint"> ${pattern ? html`<span style="font-weight:700">Pattern: </span>${pattern}<br>` : ''} ${constraints.length ? html`<span style="font-weight:700">Constraints: </span>${constraints.join(', ')}<br>` : ''} ${allowedValues === null || allowedValues === void 0 ? void 0 : allowedValues.split('┃').filter(v => v !== '').map((v, i) => html` ${i > 0 ? '|' : html`<span style="font-weight:700">Allowed: </span>`} ${html` <a part="anchor anchor-param-constraint" data-type="${type === 'array' ? type : 'string'}" data-enum="${v.trim()}" @click="${e => {
|
|
67
117
|
const inputEl = e.target.closest('table').querySelector(`[data-pname="${keyLabel}"]`);
|
|
68
118
|
|
|
69
119
|
if (inputEl) {
|
|
@@ -75,7 +125,17 @@ function generateFormRows(data, options, dataType = 'object', key = '', descript
|
|
|
75
125
|
if (inputEl) {
|
|
76
126
|
inputEl.value = e.target.dataset.exampleType === 'array' ? e.target.dataset.example.split('~|~') : e.target.dataset.example;
|
|
77
127
|
}
|
|
78
|
-
}}"> ${type === 'array' ? example.join(', ') : example} </a> ${type === 'array' ? '] ' : ''} </span>` : ''} </td> </tr>` : ''}
|
|
128
|
+
}}"> ${type === 'array' ? example.join(', ') : example} </a> ${type === 'array' ? '] ' : ''} </span>` : ''} </td> </tr>` : ''}`);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function inputFieldKeyLabel(isOption, keyLabel, keyDescription, dataType, deprecated, isRequired, schemaTitle, format, rowGenerator) {
|
|
132
|
+
if (isPatternProperty(keyLabel)) {
|
|
133
|
+
return html` <td style="width:160px;min-width:100px"> <div class="param-name ${deprecated ? 'deprecated' : ''}"> <input placeholder="${keyLabel}" @change="${e => {
|
|
134
|
+
rowGenerator(e);
|
|
135
|
+
}}" .value="${''}" spellcheck="false" type="${format === 'binary' ? 'file' : format === 'password' ? 'password' : 'text'}" part="textbox textbox-param" style="width:100%" data-ptype="pattern-property-key" data-pname="${keyLabel}" data-default="${''}" data-array="false"> </div></td>`;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return html` <td style="width:160px;min-width:100px"> <div class="param-name ${deprecated ? 'deprecated' : ''}"> ${!deprecated && isRequired ? html`<span class="key-label">${keyLabel}</span><span style="color:var(--red)">*</span>` : isOption ? html`<span class="xxx-of-key">${keyLabel}</span><span class="xxx-of-descr">${keyDescription}</span>` : html`${keyLabel ? html`<span class="key-label"> ${keyLabel}</span>` : html`<span class="xxx-of-descr">${schemaTitle}</span>`}`} </div> <div class="param-type"> ${dataType === 'array' ? html`[<span>${format}</span>]` : `${format}`} </div> </td>`;
|
|
79
139
|
} // function getObjectFormField(keyLabel, example, defaultValue, format, options) {
|
|
80
140
|
// return html`
|
|
81
141
|
// <td>
|
|
@@ -86,30 +146,36 @@ function generateFormRows(data, options, dataType = 'object', key = '', descript
|
|
|
86
146
|
// part = "textarea textarea-param"
|
|
87
147
|
// style = "width:100%; border:none; resize:vertical;"
|
|
88
148
|
// data-array = "false"
|
|
89
|
-
// data-ptype = "
|
|
149
|
+
// data-ptype = "form-input"
|
|
90
150
|
// data-pname = "${keyLabel}"
|
|
91
151
|
// data-default = "${defaultValue || ''}"
|
|
92
152
|
// spellcheck = "false"
|
|
93
153
|
// .value="${options.fillRequestWithDefault === 'true' ? defaultValue : ''}"
|
|
94
154
|
// ></textarea>
|
|
95
155
|
// <!-- This textarea(hidden) is to store the original example value, in focused mode on navbar change it is used to update the example text -->
|
|
96
|
-
// <textarea data-pname = "hidden-${keyLabel}" data-ptype = "
|
|
156
|
+
// <textarea data-pname = "hidden-${keyLabel}" data-ptype = "hidden-form-input" class="is-hidden" style="display:none" .value="${defaultValue}"></textarea>
|
|
97
157
|
// </div>
|
|
98
158
|
// </div>
|
|
99
159
|
// </td>`;
|
|
100
160
|
// }
|
|
101
161
|
|
|
102
162
|
|
|
103
|
-
function getArrayFormField(keyLabel, example, defaultValue, format,
|
|
163
|
+
function getArrayFormField(keyLabel, example, defaultValue, format, rowGenerator) {
|
|
104
164
|
if (format === 'binary') {
|
|
105
|
-
return html`<td style="min-width:100px"> <div class="file-input-container col" style="align-items:flex-end" @click="${e => this.onAddRemoveFileInput(e, keyLabel
|
|
165
|
+
return html`<td style="min-width:100px"> <div class="file-input-container col" style="align-items:flex-end" @click="${e => this.onAddRemoveFileInput(e, keyLabel)}"> <div class="input-set row"> <input @change="${e => {
|
|
166
|
+
rowGenerator(e);
|
|
167
|
+
}}" type="file" part="file-input" class="file-input" data-pname="${keyLabel}" data-ptype="form-input" data-array="false" data-file-array="true"> <button class="file-input-remove-btn"> ✕ </button> </div> <button class="m-btn primary file-input-add-btn" part="btn btn-fill" style="margin:2px 25px 0 0;padding:2px 6px">ADD</button> </div> </td>`;
|
|
106
168
|
}
|
|
107
169
|
|
|
108
|
-
return html`<td style="min-width:100px"> <tag-input
|
|
170
|
+
return html`<td style="min-width:100px"> <tag-input @change="${e => {
|
|
171
|
+
rowGenerator(e);
|
|
172
|
+
}}" style="width:100%" data-ptype="form-input" data-pname="${keyLabel}" data-default="${defaultValue || ''}" data-array="true" placeholder="${(Array.isArray(example) ? example[0] : example) || defaultValue || 'add-multiple ↩'}" .value="${defaultValue || ''}"></tag-input> </td>`;
|
|
109
173
|
}
|
|
110
174
|
|
|
111
|
-
function getPrimitiveFormField(keyLabel, example, defaultValue, format, options) {
|
|
112
|
-
return html`<td style="min-width:100px"> <input placeholder="${example || defaultValue || ''}"
|
|
175
|
+
function getPrimitiveFormField(keyLabel, example, defaultValue, format, options, rowGenerator) {
|
|
176
|
+
return html`<td style="min-width:100px"> <input placeholder="${example || defaultValue || ''}" @change="${e => {
|
|
177
|
+
rowGenerator(e);
|
|
178
|
+
}}" .value="${options.fillRequestWithDefault && defaultValue || ''}" spellcheck="false" type="${format === 'binary' ? 'file' : format === 'password' ? 'password' : 'text'}" part="textbox textbox-param" style="width:100%" data-ptype="form-input" data-pname="${keyLabel}" data-default="${defaultValue || ''}" data-array="false"> </td>`;
|
|
113
179
|
}
|
|
114
180
|
|
|
115
181
|
export default function getRequestFormTable(data, mimeType) {
|
|
@@ -117,5 +183,5 @@ export default function getRequestFormTable(data, mimeType) {
|
|
|
117
183
|
mimeType: mimeType,
|
|
118
184
|
fillRequestWithDefault: this.fillRequestWithDefault === 'true'
|
|
119
185
|
};
|
|
120
|
-
return html` <table role="presentation" class="request-form-table" style="border:1px solid var(--light-border-color);width:100%"> ${data ? html`${generateFormRows.call(this, data['::type'] === 'array' ? data['::props'] : data, options, data['::type'])}` : ''} </table>`;
|
|
186
|
+
return html` <table id="request-form-table" role="presentation" class="request-form-table" style="border:1px solid var(--light-border-color);width:100%"> ${data ? html`${generateFormRows.call(this, data['::type'] === 'array' ? data['::props'] : data, options, data['::type'])}` : ''} </table>`;
|
|
121
187
|
}
|
|
@@ -385,6 +385,10 @@ function getSimpleValueResult(schema, config, namespace, prefix, xmlAttributes,
|
|
|
385
385
|
const value = getSampleValueByType(schema, config.propertyName, config.skipExampleStrings);
|
|
386
386
|
return [value];
|
|
387
387
|
}
|
|
388
|
+
|
|
389
|
+
export function isPatternProperty(label) {
|
|
390
|
+
return label.match(/^<any-key>|<pattern:/);
|
|
391
|
+
}
|
|
388
392
|
/**
|
|
389
393
|
* For changing OpenAPI-Schema to an Object Notation,
|
|
390
394
|
* This Object would further be an input to UI Components to generate an Object-Tree
|
|
@@ -394,7 +398,6 @@ function getSimpleValueResult(schema, config, namespace, prefix, xmlAttributes,
|
|
|
394
398
|
* @param {string} suffix - used for suffixing property names to avoid duplicate props during object composition
|
|
395
399
|
*/
|
|
396
400
|
|
|
397
|
-
|
|
398
401
|
export function schemaInObjectNotation(rawSchema, options, level = 0, suffix = '') {
|
|
399
402
|
if (!rawSchema) {
|
|
400
403
|
return undefined;
|