rapidoc 9.3.7 → 10.0.0
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/README.md +193 -61
- package/dist/rapidoc-min.js +279 -3912
- package/package.json +24 -47
- package/.babelrc.json +0 -42
- package/.eslintrc +0 -53
- package/.github/FUNDING.yml +0 -13
- package/.github/workflow/words.yaml +0 -22
- package/CONTRIBUTING.md +0 -35
- package/dist/index.html +0 -6
- package/dist/index.html.gz +0 -0
- package/dist/rapidoc-min.js.LICENSE.txt +0 -64
- package/dist/rapidoc-min.js.LICENSE.txt.gz +0 -0
- package/dist/rapidoc-min.js.gz +0 -0
- package/dist/rapidoc-min.js.map +0 -1
- package/dist/rapidoc-min.js.map.gz +0 -0
- package/dist/rapidoc.js +0 -21594
- package/dist/report.html +0 -39
- package/index-hashroute.html +0 -65
- package/index.html +0 -249
- package/index1.html +0 -244
- package/jsconfig.json +0 -11
- package/logo.png +0 -0
- package/performance.html +0 -31
- package/src/components/api-request.js +0 -1787
- package/src/components/api-response.js +0 -335
- package/src/components/dialog-box.js +0 -73
- package/src/components/json-tree.js +0 -145
- package/src/components/schema-table.js +0 -358
- package/src/components/schema-tree.js +0 -378
- package/src/components/tag-input.js +0 -135
- package/src/index.js +0 -7
- package/src/json-schema-viewer.js +0 -345
- package/src/oauth-receiver.js +0 -56
- package/src/rapidoc-mini.js +0 -380
- package/src/rapidoc.js +0 -1082
- package/src/styles/border-styles.js +0 -24
- package/src/styles/custom-styles.js +0 -10
- package/src/styles/dialog-box-styles.js +0 -75
- package/src/styles/endpoint-styles.js +0 -202
- package/src/styles/flex-styles.js +0 -15
- package/src/styles/font-styles.js +0 -268
- package/src/styles/info-styles.js +0 -21
- package/src/styles/input-styles.js +0 -202
- package/src/styles/nav-styles.js +0 -198
- package/src/styles/prism-styles.js +0 -105
- package/src/styles/schema-styles.js +0 -121
- package/src/styles/tab-styles.js +0 -47
- package/src/styles/table-styles.js +0 -58
- package/src/templates/advance-search-template.js +0 -72
- package/src/templates/callback-template.js +0 -73
- package/src/templates/code-samples-template.js +0 -34
- package/src/templates/components-template.js +0 -76
- package/src/templates/endpoint-template.js +0 -249
- package/src/templates/expanded-endpoint-template.js +0 -197
- package/src/templates/focused-endpoint-template.js +0 -110
- package/src/templates/header-template.js +0 -66
- package/src/templates/json-schema-viewer-template.js +0 -185
- package/src/templates/logo-template.js +0 -18
- package/src/templates/main-body-template.js +0 -121
- package/src/templates/navbar-template.js +0 -272
- package/src/templates/overview-template.js +0 -79
- package/src/templates/security-scheme-template.js +0 -610
- package/src/templates/server-template.js +0 -115
- package/src/utils/color-utils.js +0 -59
- package/src/utils/common-utils.js +0 -168
- package/src/utils/schema-utils.js +0 -1086
- package/src/utils/spec-parser.js +0 -462
- package/src/utils/theme.js +0 -288
- package/webpack.config.js +0 -142
|
@@ -1,1787 +0,0 @@
|
|
|
1
|
-
import { LitElement, html, css } from 'lit';
|
|
2
|
-
import { unsafeHTML } from 'lit/directives/unsafe-html.js'; // eslint-disable-line import/extensions
|
|
3
|
-
import { guard } from 'lit/directives/guard.js'; // eslint-disable-line import/extensions
|
|
4
|
-
import { live } from 'lit/directives/live.js'; // eslint-disable-line import/extensions
|
|
5
|
-
import { ifDefined } from 'lit/directives/if-defined.js'; // eslint-disable-line import/extensions
|
|
6
|
-
import { marked } from 'marked';
|
|
7
|
-
import formatXml from 'xml-but-prettier';
|
|
8
|
-
import Prism from 'prismjs';
|
|
9
|
-
import TableStyles from '~/styles/table-styles';
|
|
10
|
-
import FlexStyles from '~/styles/flex-styles';
|
|
11
|
-
import InputStyles from '~/styles/input-styles';
|
|
12
|
-
import FontStyles from '~/styles/font-styles';
|
|
13
|
-
import BorderStyles from '~/styles/border-styles';
|
|
14
|
-
import TabStyles from '~/styles/tab-styles';
|
|
15
|
-
import PrismStyles from '~/styles/prism-styles';
|
|
16
|
-
import CustomStyles from '~/styles/custom-styles';
|
|
17
|
-
import { copyToClipboard, downloadResource, viewResource } from '~/utils/common-utils';
|
|
18
|
-
import { schemaInObjectNotation,
|
|
19
|
-
getTypeInfo,
|
|
20
|
-
generateExample,
|
|
21
|
-
normalizeExamples,
|
|
22
|
-
getSchemaFromParam,
|
|
23
|
-
json2xml,
|
|
24
|
-
standardizeExample,
|
|
25
|
-
anyExampleWithSummaryOrDescription } from '~/utils/schema-utils';
|
|
26
|
-
import '~/components/json-tree';
|
|
27
|
-
import '~/components/schema-tree';
|
|
28
|
-
import '~/components/tag-input';
|
|
29
|
-
|
|
30
|
-
export default class ApiRequest extends LitElement {
|
|
31
|
-
constructor() {
|
|
32
|
-
super();
|
|
33
|
-
this.responseMessage = '';
|
|
34
|
-
this.responseStatus = 'success';
|
|
35
|
-
this.responseHeaders = '';
|
|
36
|
-
this.responseText = '';
|
|
37
|
-
this.responseUrl = '';
|
|
38
|
-
this.curlSyntax = '';
|
|
39
|
-
this.activeResponseTab = 'response'; // allowed values: response, headers, curl
|
|
40
|
-
this.selectedRequestBodyType = '';
|
|
41
|
-
this.selectedRequestBodyExample = '';
|
|
42
|
-
this.activeParameterSchemaTabs = {};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
static get properties() {
|
|
46
|
-
return {
|
|
47
|
-
serverUrl: { type: String, attribute: 'server-url' },
|
|
48
|
-
servers: { type: Array },
|
|
49
|
-
method: { type: String },
|
|
50
|
-
path: { type: String },
|
|
51
|
-
security: { type: Array },
|
|
52
|
-
parameters: { type: Array },
|
|
53
|
-
request_body: { type: Object },
|
|
54
|
-
api_keys: { type: Array },
|
|
55
|
-
parser: { type: Object },
|
|
56
|
-
accept: { type: String },
|
|
57
|
-
callback: { type: String },
|
|
58
|
-
webhook: { type: String },
|
|
59
|
-
responseMessage: { type: String, attribute: false },
|
|
60
|
-
responseText: { type: String, attribute: false },
|
|
61
|
-
responseHeaders: { type: String, attribute: false },
|
|
62
|
-
responseStatus: { type: String, attribute: false },
|
|
63
|
-
responseUrl: { type: String, attribute: false },
|
|
64
|
-
curlSyntax: { type: String, attribute: false },
|
|
65
|
-
fillRequestFieldsWithExample: { type: String, attribute: 'fill-request-fields-with-example' },
|
|
66
|
-
allowTry: { type: String, attribute: 'allow-try' },
|
|
67
|
-
showCurlBeforeTry: { type: String, attribute: 'show-curl-before-try' },
|
|
68
|
-
renderStyle: { type: String, attribute: 'render-style' },
|
|
69
|
-
schemaStyle: { type: String, attribute: 'schema-style' },
|
|
70
|
-
activeSchemaTab: { type: String, attribute: 'active-schema-tab' },
|
|
71
|
-
activeParameterSchemaTabs: {
|
|
72
|
-
type: Object,
|
|
73
|
-
converter: {
|
|
74
|
-
fromAttribute: (attr) => JSON.parse(attr),
|
|
75
|
-
toAttribute: (prop) => JSON.stringify(prop),
|
|
76
|
-
},
|
|
77
|
-
attribute: 'active-parameter-schema-tabs',
|
|
78
|
-
},
|
|
79
|
-
schemaExpandLevel: { type: Number, attribute: 'schema-expand-level' },
|
|
80
|
-
schemaDescriptionExpanded: { type: String, attribute: 'schema-description-expanded' },
|
|
81
|
-
allowSchemaDescriptionExpandToggle: { type: String, attribute: 'allow-schema-description-expand-toggle' },
|
|
82
|
-
schemaHideReadOnly: { type: String, attribute: 'schema-hide-read-only' },
|
|
83
|
-
schemaHideWriteOnly: { type: String, attribute: 'schema-hide-write-only' },
|
|
84
|
-
fetchCredentials: { type: String, attribute: 'fetch-credentials' },
|
|
85
|
-
|
|
86
|
-
// properties for internal tracking
|
|
87
|
-
activeResponseTab: { type: String }, // internal tracking of response-tab not exposed as a attribute
|
|
88
|
-
selectedRequestBodyType: { type: String, attribute: 'selected-request-body-type' }, // internal tracking of selected request-body type
|
|
89
|
-
selectedRequestBodyExample: { type: String, attribute: 'selected-request-body-example' }, // internal tracking of selected request-body example
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
static get styles() {
|
|
94
|
-
return [
|
|
95
|
-
TableStyles,
|
|
96
|
-
InputStyles,
|
|
97
|
-
FontStyles,
|
|
98
|
-
FlexStyles,
|
|
99
|
-
BorderStyles,
|
|
100
|
-
TabStyles,
|
|
101
|
-
PrismStyles,
|
|
102
|
-
css`
|
|
103
|
-
:host {
|
|
104
|
-
container-type: inline-size;
|
|
105
|
-
}
|
|
106
|
-
*, *:before, *:after { box-sizing: border-box; }
|
|
107
|
-
:where(button, input[type="checkbox"], [tabindex="0"]):focus-visible { box-shadow: var(--focus-shadow); }
|
|
108
|
-
:where(input[type="text"], input[type="password"], select, textarea):focus-visible { border-color: var(--primary-color); }
|
|
109
|
-
tag-input:focus-within { outline: 1px solid;}
|
|
110
|
-
.read-mode {
|
|
111
|
-
margin-top: 24px;
|
|
112
|
-
}
|
|
113
|
-
.param-name,
|
|
114
|
-
.param-type {
|
|
115
|
-
margin: 1px 0;
|
|
116
|
-
text-align: right;
|
|
117
|
-
line-height: var(--font-size-small);
|
|
118
|
-
}
|
|
119
|
-
.param-name {
|
|
120
|
-
color: var(--fg);
|
|
121
|
-
font-family: var(--font-mono);
|
|
122
|
-
}
|
|
123
|
-
.param-name.deprecated {
|
|
124
|
-
color: var(--red);
|
|
125
|
-
}
|
|
126
|
-
.param-type{
|
|
127
|
-
color: var(--light-fg);
|
|
128
|
-
font-family: var(--font-regular);
|
|
129
|
-
}
|
|
130
|
-
.param-constraint{
|
|
131
|
-
min-width:100px;
|
|
132
|
-
}
|
|
133
|
-
.param-constraint:empty{
|
|
134
|
-
display:none;
|
|
135
|
-
}
|
|
136
|
-
.top-gap{margin-top:24px;}
|
|
137
|
-
|
|
138
|
-
.textarea {
|
|
139
|
-
min-height:220px;
|
|
140
|
-
padding:5px;
|
|
141
|
-
resize:vertical;
|
|
142
|
-
direction: ltr;
|
|
143
|
-
}
|
|
144
|
-
.example:first-child {
|
|
145
|
-
margin-top: -9px;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
.response-message{
|
|
149
|
-
font-weight:bold;
|
|
150
|
-
text-overflow: ellipsis;
|
|
151
|
-
}
|
|
152
|
-
.response-message.error {
|
|
153
|
-
color:var(--red);
|
|
154
|
-
}
|
|
155
|
-
.response-message.success {
|
|
156
|
-
color:var(--blue);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
.file-input-container {
|
|
160
|
-
align-items:flex-end;
|
|
161
|
-
}
|
|
162
|
-
.file-input-container .input-set:first-child .file-input-remove-btn{
|
|
163
|
-
visibility:hidden;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
.file-input-remove-btn{
|
|
167
|
-
font-size:16px;
|
|
168
|
-
color:var(--red);
|
|
169
|
-
outline: none;
|
|
170
|
-
border: none;
|
|
171
|
-
background:none;
|
|
172
|
-
cursor:pointer;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
.v-tab-btn {
|
|
176
|
-
font-size: var(--smal-font-size);
|
|
177
|
-
height:24px;
|
|
178
|
-
border:none;
|
|
179
|
-
background:none;
|
|
180
|
-
opacity: 0.3;
|
|
181
|
-
cursor: pointer;
|
|
182
|
-
padding: 4px 8px;
|
|
183
|
-
}
|
|
184
|
-
.v-tab-btn.active {
|
|
185
|
-
font-weight: bold;
|
|
186
|
-
background: var(--bg);
|
|
187
|
-
opacity: 1;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
@container (min-width: 768px) {
|
|
191
|
-
.textarea {
|
|
192
|
-
padding:8px;
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
@container (max-width: 470px) {
|
|
197
|
-
.hide-in-small-screen {
|
|
198
|
-
display:none;
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
`,
|
|
202
|
-
CustomStyles,
|
|
203
|
-
];
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
render() {
|
|
207
|
-
return html`
|
|
208
|
-
<div class="col regular-font request-panel ${'read focused'.includes(this.renderStyle) || this.callback === 'true' ? 'read-mode' : 'view-mode'}">
|
|
209
|
-
<div class=" ${this.callback === 'true' ? 'tiny-title' : 'req-res-title'} ">
|
|
210
|
-
${this.callback === 'true' ? 'CALLBACK REQUEST' : 'REQUEST'}
|
|
211
|
-
</div>
|
|
212
|
-
<div>
|
|
213
|
-
${guard([this.method, this.path, this.allowTry, this.parameters, this.activeParameterSchemaTabs], () => this.inputParametersTemplate('path'))}
|
|
214
|
-
${guard([this.method, this.path, this.allowTry, this.parameters, this.activeParameterSchemaTabs], () => this.inputParametersTemplate('query'))}
|
|
215
|
-
${this.requestBodyTemplate()}
|
|
216
|
-
${guard([this.method, this.path, this.allowTry, this.parameters, this.activeParameterSchemaTabs], () => this.inputParametersTemplate('header'))}
|
|
217
|
-
${guard([this.method, this.path, this.allowTry, this.parameters, this.activeParameterSchemaTabs], () => this.inputParametersTemplate('cookie'))}
|
|
218
|
-
${this.allowTry === 'false' ? '' : html`${this.apiCallTemplate()}`}
|
|
219
|
-
</div>
|
|
220
|
-
</div>
|
|
221
|
-
`;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
async updated() {
|
|
225
|
-
if (this.showCurlBeforeTry === 'true') {
|
|
226
|
-
this.applyCURLSyntax(this.shadowRoot);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
// In focused mode after rendering the request component, update the text-areas(which contains examples) using
|
|
230
|
-
// the original values from hidden textareas
|
|
231
|
-
// 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
|
|
232
|
-
|
|
233
|
-
// if (this.renderStyle === 'focused') {
|
|
234
|
-
// if (changedProperties.size === 1 && changedProperties.has('activeSchemaTab')) {
|
|
235
|
-
// // dont update example as only tabs is switched
|
|
236
|
-
// } else {
|
|
237
|
-
// this.requestUpdate();
|
|
238
|
-
// }
|
|
239
|
-
// }
|
|
240
|
-
|
|
241
|
-
if (this.webhook === 'true') {
|
|
242
|
-
this.allowTry = 'false';
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
async saveExampleState() {
|
|
247
|
-
if (this.renderStyle === 'focused') {
|
|
248
|
-
const reqBodyTextAreaEls = [...this.shadowRoot.querySelectorAll('textarea.request-body-param-user-input')];
|
|
249
|
-
reqBodyTextAreaEls.forEach((el) => {
|
|
250
|
-
el.dataset.user_example = el.value;
|
|
251
|
-
});
|
|
252
|
-
const exampleTextAreaEls = [...this.shadowRoot.querySelectorAll('textarea[data-ptype="form-data"]')];
|
|
253
|
-
exampleTextAreaEls.forEach((el) => {
|
|
254
|
-
el.dataset.user_example = el.value;
|
|
255
|
-
});
|
|
256
|
-
this.requestUpdate();
|
|
257
|
-
}
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
async updateExamplesFromDataAttr() {
|
|
261
|
-
// In focused mode after rendering the request component, update the text-areas(which contains examples) using
|
|
262
|
-
// the original values from hidden textareas
|
|
263
|
-
// 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
|
|
264
|
-
if (this.renderStyle === 'focused') {
|
|
265
|
-
const reqBodyTextAreaEls = [...this.shadowRoot.querySelectorAll('textarea.request-body-param-user-input')];
|
|
266
|
-
reqBodyTextAreaEls.forEach((el) => {
|
|
267
|
-
el.value = el.dataset.user_example || el.dataset.example;
|
|
268
|
-
});
|
|
269
|
-
const exampleTextAreaEls = [...this.shadowRoot.querySelectorAll('textarea[data-ptype="form-data"]')];
|
|
270
|
-
exampleTextAreaEls.forEach((el) => {
|
|
271
|
-
el.value = el.dataset.user_example || el.dataset.example;
|
|
272
|
-
});
|
|
273
|
-
this.requestUpdate();
|
|
274
|
-
}
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/* eslint-disable indent */
|
|
278
|
-
renderExample(example, paramType, paramName) {
|
|
279
|
-
return html`
|
|
280
|
-
${paramType === 'array' ? '[' : ''}
|
|
281
|
-
<a
|
|
282
|
-
part="anchor anchor-param-example"
|
|
283
|
-
style="display:inline-block; min-width:24px; text-align:center"
|
|
284
|
-
class="${this.allowTry === 'true' ? '' : 'inactive-link'}"
|
|
285
|
-
data-example-type="${paramType === 'array' ? paramType : 'string'}"
|
|
286
|
-
data-example="${example.value && Array.isArray(example.value) ? example.value?.join('~|~') : (typeof example.value === 'object' ? JSON.stringify(example.value, null, 2) : example.value) || ''}"
|
|
287
|
-
title="${example.value && Array.isArray(example.value) ? example.value?.join('~|~') : (typeof example.value === 'object' ? JSON.stringify(example.value, null, 2) : example.value) || ''}"
|
|
288
|
-
@click="${(e) => {
|
|
289
|
-
const inputEl = e.target.closest('table').querySelector(`[data-pname="${paramName}"]`);
|
|
290
|
-
if (inputEl) {
|
|
291
|
-
inputEl.value = e.target.dataset.exampleType === 'array' ? e.target.dataset.example.split('~|~') : e.target.dataset.example;
|
|
292
|
-
}
|
|
293
|
-
}}"
|
|
294
|
-
> ${example.printableValue || example.value} </a>
|
|
295
|
-
${paramType === 'array' ? '] ' : ''}
|
|
296
|
-
`;
|
|
297
|
-
}
|
|
298
|
-
|
|
299
|
-
renderShortFormatExamples(examples, paramType, paramName) {
|
|
300
|
-
return html`${examples.map((x, i) => html`
|
|
301
|
-
${i === 0 ? '' : '┃'}
|
|
302
|
-
${this.renderExample(x, paramType, paramName)}`)}`;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
renderLongFormatExamples(exampleList, paramType, paramName) {
|
|
306
|
-
return html` <ul style="list-style-type: disclosure-closed;">
|
|
307
|
-
${exampleList.map((v) => html`
|
|
308
|
-
<li>
|
|
309
|
-
${this.renderExample(v, paramType, paramName)}
|
|
310
|
-
${v.summary?.length > 0 ? html`<span>(${v.summary})</span>` : ''}
|
|
311
|
-
${v.description?.length > 0 ? html`<p>${unsafeHTML(marked(v.description))}</p>` : ''}
|
|
312
|
-
</li>
|
|
313
|
-
`)}
|
|
314
|
-
</ul>`;
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
exampleListTemplate(paramName, paramType, exampleList = []) {
|
|
318
|
-
return html` ${
|
|
319
|
-
exampleList.length > 0
|
|
320
|
-
? html`<span style="font-weight:bold">Examples: </span>
|
|
321
|
-
${anyExampleWithSummaryOrDescription(exampleList)
|
|
322
|
-
? this.renderLongFormatExamples(exampleList, paramType, paramName)
|
|
323
|
-
: this.renderShortFormatExamples(exampleList, paramType, paramName)}`
|
|
324
|
-
: ''
|
|
325
|
-
}`;
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
inputParametersTemplate(paramType) {
|
|
329
|
-
const filteredParams = this.parameters ? this.parameters.filter((param) => param.in === paramType) : [];
|
|
330
|
-
if (filteredParams.length === 0) {
|
|
331
|
-
return '';
|
|
332
|
-
}
|
|
333
|
-
let title = '';
|
|
334
|
-
if (paramType === 'path') {
|
|
335
|
-
title = 'PATH PARAMETERS';
|
|
336
|
-
} else if (paramType === 'query') {
|
|
337
|
-
title = 'QUERY-STRING PARAMETERS';
|
|
338
|
-
} else if (paramType === 'header') {
|
|
339
|
-
title = 'REQUEST HEADERS';
|
|
340
|
-
} else if (paramType === 'cookie') {
|
|
341
|
-
title = 'COOKIES';
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
const tableRows = [];
|
|
345
|
-
for (const param of filteredParams) {
|
|
346
|
-
const [declaredParamSchema, serializeStyle, mimeTypeElem] = getSchemaFromParam(param);
|
|
347
|
-
if (!declaredParamSchema) {
|
|
348
|
-
continue;
|
|
349
|
-
}
|
|
350
|
-
const paramSchema = getTypeInfo(declaredParamSchema);
|
|
351
|
-
if (!paramSchema) {
|
|
352
|
-
continue; // eslint-disable-line no-continue
|
|
353
|
-
}
|
|
354
|
-
const schemaAsObj = schemaInObjectNotation(declaredParamSchema, {});
|
|
355
|
-
// let exampleVal = '';
|
|
356
|
-
// let exampleList = [];
|
|
357
|
-
let paramStyle = 'form';
|
|
358
|
-
let paramExplode = true;
|
|
359
|
-
let paramAllowReserved = false;
|
|
360
|
-
if (paramType === 'query' || paramType === 'header' || paramType === 'path') {
|
|
361
|
-
if (param.style && 'form spaceDelimited pipeDelimited'.includes(param.style)) {
|
|
362
|
-
paramStyle = param.style;
|
|
363
|
-
} else if (serializeStyle) {
|
|
364
|
-
paramStyle = serializeStyle;
|
|
365
|
-
}
|
|
366
|
-
if (typeof param.explode === 'boolean') {
|
|
367
|
-
paramExplode = param.explode;
|
|
368
|
-
}
|
|
369
|
-
if (typeof param.allowReserved === 'boolean') {
|
|
370
|
-
paramAllowReserved = param.allowReserved;
|
|
371
|
-
}
|
|
372
|
-
}
|
|
373
|
-
// openapi 3.1.0 spec based examples (which must be Object(string : { value:any, summary?: string, description?: string})
|
|
374
|
-
const example = normalizeExamples(
|
|
375
|
-
(standardizeExample(param.examples)
|
|
376
|
-
|| standardizeExample(param.example)
|
|
377
|
-
|| standardizeExample(mimeTypeElem?.example)
|
|
378
|
-
|| standardizeExample(mimeTypeElem?.examples)
|
|
379
|
-
|| standardizeExample(paramSchema.examples)
|
|
380
|
-
|| standardizeExample(paramSchema.example)
|
|
381
|
-
),
|
|
382
|
-
paramSchema.type,
|
|
383
|
-
);
|
|
384
|
-
if (!example.exampleVal && paramSchema.type === 'object') {
|
|
385
|
-
example.exampleVal = generateExample(
|
|
386
|
-
declaredParamSchema,
|
|
387
|
-
serializeStyle || 'json',
|
|
388
|
-
{},
|
|
389
|
-
{},
|
|
390
|
-
this.callback === 'true' || this.webhook === 'true' ? true : false, // eslint-disable-line no-unneeded-ternary
|
|
391
|
-
this.callback === 'true' || this.webhook === 'true' ? false : true, // eslint-disable-line no-unneeded-ternary
|
|
392
|
-
true,
|
|
393
|
-
'text',
|
|
394
|
-
false,
|
|
395
|
-
)[0].exampleValue;
|
|
396
|
-
}
|
|
397
|
-
const labelColWidth = 'read focused'.includes(this.renderStyle) ? '200px' : '160px';
|
|
398
|
-
tableRows.push(html`
|
|
399
|
-
<tr title="${param.deprecated ? 'Deprecated' : ''}">
|
|
400
|
-
<td rowspan="${this.allowTry === 'true' ? '1' : '2'}" style="width:${labelColWidth}; min-width:100px;">
|
|
401
|
-
<div class="param-name ${param.deprecated ? 'deprecated' : ''}" >
|
|
402
|
-
${param.deprecated ? html`<span style='color:var(--red);'>✗</span>` : ''}
|
|
403
|
-
${param.required ? html`<span style='color:var(--red)'>*</span>` : ''}
|
|
404
|
-
${param.name}
|
|
405
|
-
</div>
|
|
406
|
-
<div class="param-type">
|
|
407
|
-
${paramSchema.type === 'array'
|
|
408
|
-
? `${paramSchema.arrayType}`
|
|
409
|
-
: `${paramSchema.format ? paramSchema.format : paramSchema.type}`
|
|
410
|
-
}
|
|
411
|
-
</div>
|
|
412
|
-
</td>
|
|
413
|
-
${this.allowTry === 'true'
|
|
414
|
-
? html`
|
|
415
|
-
<td style="min-width:100px;" colspan="${paramSchema.default || paramSchema.constrain || paramSchema.allowedValues || paramSchema.pattern ? '1' : '2'}">
|
|
416
|
-
${paramSchema.type === 'array'
|
|
417
|
-
? html`
|
|
418
|
-
<tag-input class="request-param"
|
|
419
|
-
id = "tag-input-request-param-${param.name}"
|
|
420
|
-
style = "width:100%"
|
|
421
|
-
data-ptype = "${paramType}"
|
|
422
|
-
data-pname = "${param.name}"
|
|
423
|
-
data-example = "${Array.isArray(example.exampleVal) ? example.exampleVal.join('~|~') : example.exampleVal}"
|
|
424
|
-
data-param-serialize-style = "${paramStyle}"
|
|
425
|
-
data-param-serialize-explode = "${paramExplode}"
|
|
426
|
-
data-param-allow-reserved = "${paramAllowReserved}"
|
|
427
|
-
data-x-fill-example = "${param['x-fill-example'] || 'yes'}"
|
|
428
|
-
data-array = "true"
|
|
429
|
-
placeholder = "add-multiple ↩"
|
|
430
|
-
.value="${param['x-fill-example'] === 'no'
|
|
431
|
-
? []
|
|
432
|
-
: live(this.fillRequestFieldsWithExample === 'true' ? Array.isArray(example.exampleVal) ? example.exampleVal : [example.exampleVal] : [])
|
|
433
|
-
}"
|
|
434
|
-
>
|
|
435
|
-
</tag-input>`
|
|
436
|
-
: paramSchema.type === 'object'
|
|
437
|
-
? html`
|
|
438
|
-
<div class="tab-panel col" style="border-width:0 0 1px 0;">
|
|
439
|
-
<div class="tab-buttons row" @click="${(e) => {
|
|
440
|
-
if (e.target.tagName.toLowerCase() === 'button') {
|
|
441
|
-
const newState = { ...this.activeParameterSchemaTabs };
|
|
442
|
-
newState[param.name] = e.target.dataset.tab;
|
|
443
|
-
this.activeParameterSchemaTabs = newState;
|
|
444
|
-
}
|
|
445
|
-
}}">
|
|
446
|
-
<button class="tab-btn ${this.activeParameterSchemaTabs[param.name] === 'example' ? 'active' : ''}" data-tab = 'example'>EXAMPLE </button>
|
|
447
|
-
<button class="tab-btn ${this.activeParameterSchemaTabs[param.name] !== 'example' ? 'active' : ''}" data-tab = 'schema'>SCHEMA</button>
|
|
448
|
-
</div>
|
|
449
|
-
|
|
450
|
-
${html`<div class="tab-content col" data-tab = 'example' style="display:${this.activeParameterSchemaTabs[param.name] === 'example' ? 'block' : 'none'}; padding-left:5px; width:100%">
|
|
451
|
-
<textarea
|
|
452
|
-
id = "textarea-request-param-${param.name}"
|
|
453
|
-
class = "textarea request-param"
|
|
454
|
-
part = "textarea textarea-param"
|
|
455
|
-
data-ptype = "${paramType}-object"
|
|
456
|
-
data-pname = "${param.name}"
|
|
457
|
-
data-example = "${example.exampleVal}"
|
|
458
|
-
data-param-serialize-style = "${paramStyle}"
|
|
459
|
-
data-param-serialize-explode = "${paramExplode}"
|
|
460
|
-
data-param-allow-reserved = "${paramAllowReserved}"
|
|
461
|
-
data-x-fill-example = "${param['x-fill-example'] || 'yes'}"
|
|
462
|
-
spellcheck = "false"
|
|
463
|
-
.textContent="${param['x-fill-example'] === 'no' ? '' : live(this.fillRequestFieldsWithExample === 'true' ? (typeof example.exampleVal === 'object' ? JSON.stringify(example.exampleVal, null, 2) : example.exampleVal) : '')}"
|
|
464
|
-
style = "resize:vertical; width:100%; height: ${'read focused'.includes(this.renderStyle) ? '180px' : '120px'};"
|
|
465
|
-
@input=${(e) => {
|
|
466
|
-
const requestPanelEl = this.getRequestPanel(e);
|
|
467
|
-
this.liveCURLSyntaxUpdate(requestPanelEl);
|
|
468
|
-
}}
|
|
469
|
-
></textarea>
|
|
470
|
-
</div>`
|
|
471
|
-
}
|
|
472
|
-
${html`<div class="tab-content col" data-tab = 'schema' style="display:${this.activeParameterSchemaTabs[param.name] !== 'example' ? 'block' : 'none'}; padding-left:5px; width:100%;">
|
|
473
|
-
<schema-tree
|
|
474
|
-
class = 'json'
|
|
475
|
-
style = 'display: block'
|
|
476
|
-
.data = '${schemaAsObj}'
|
|
477
|
-
schema-expand-level = "${this.schemaExpandLevel}"
|
|
478
|
-
schema-description-expanded = "${this.schemaDescriptionExpanded}"
|
|
479
|
-
allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}"
|
|
480
|
-
schema-hide-read-only = "${this.schemaHideReadOnly.includes(this.method)}"
|
|
481
|
-
schema-hide-write-only = "${this.schemaHideWriteOnly.includes(this.method)}"
|
|
482
|
-
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,
|
|
483
|
-
file-input:file-input, textbox:textbox, textbox-param:textbox-param, textarea:textarea, textarea-param:textarea-param,
|
|
484
|
-
anchor:anchor, anchor-param-example:anchor-param-example"
|
|
485
|
-
> </schema-tree>
|
|
486
|
-
</div>`
|
|
487
|
-
}
|
|
488
|
-
</div>`
|
|
489
|
-
: html`
|
|
490
|
-
<input type="${paramSchema.format === 'password' ? 'password' : 'text'}" spellcheck="false" style="width:100%"
|
|
491
|
-
id="input-request-param-${param.name}"
|
|
492
|
-
class="request-param"
|
|
493
|
-
part="textbox textbox-param"
|
|
494
|
-
data-ptype="${paramType}"
|
|
495
|
-
data-pname="${param.name}"
|
|
496
|
-
data-example="${Array.isArray(example.exampleVal) ? example.exampleVal.join('~|~') : example.exampleVal}"
|
|
497
|
-
data-param-allow-reserved = "${paramAllowReserved}"
|
|
498
|
-
data-x-fill-example = "${param['x-fill-example'] || 'yes'}"
|
|
499
|
-
data-array="false"
|
|
500
|
-
.value="${param['x-fill-example'] === 'no' ? '' : live(this.fillRequestFieldsWithExample === 'true' ? example.exampleVal : '')}"
|
|
501
|
-
@input=${(e) => {
|
|
502
|
-
const requestPanelEl = this.getRequestPanel(e);
|
|
503
|
-
this.liveCURLSyntaxUpdate(requestPanelEl);
|
|
504
|
-
}}
|
|
505
|
-
/>`
|
|
506
|
-
}
|
|
507
|
-
</td>`
|
|
508
|
-
: ''
|
|
509
|
-
}
|
|
510
|
-
${paramSchema.default || paramSchema.constrain || paramSchema.allowedValues || paramSchema.pattern
|
|
511
|
-
? html`
|
|
512
|
-
<td colspan="${(this.allowTry === 'true') ? '1' : '2'}">
|
|
513
|
-
<div class="param-constraint">
|
|
514
|
-
${paramSchema.default ? html`<span style="font-weight:bold">Default: </span>${paramSchema.default}<br/>` : ''}
|
|
515
|
-
${paramSchema.pattern ? html`<span style="font-weight:bold">Pattern: </span>${paramSchema.pattern}<br/>` : ''}
|
|
516
|
-
${paramSchema.constrain ? html`${paramSchema.constrain}<br/>` : ''}
|
|
517
|
-
${paramSchema.allowedValues && paramSchema.allowedValues.split('┃').map((v, i) => html`
|
|
518
|
-
${i > 0 ? '┃' : html`<span style="font-weight:bold">Allowed: </span>`}
|
|
519
|
-
${html`
|
|
520
|
-
<a part="anchor anchor-param-constraint" class = "${this.allowTry === 'true' ? '' : 'inactive-link'}"
|
|
521
|
-
data-type="${paramSchema.type === 'array' ? paramSchema.type : 'string'}"
|
|
522
|
-
data-enum="${v.trim()}"
|
|
523
|
-
@click="${(e) => {
|
|
524
|
-
const inputEl = e.target.closest('table').querySelector(`[data-pname="${param.name}"]`);
|
|
525
|
-
if (inputEl) {
|
|
526
|
-
if (e.target.dataset.type === 'array') {
|
|
527
|
-
inputEl.value = [e.target.dataset.enum];
|
|
528
|
-
} else {
|
|
529
|
-
inputEl.value = e.target.dataset.enum;
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
}}"
|
|
533
|
-
>${v}</a>`
|
|
534
|
-
}`)}
|
|
535
|
-
</div>
|
|
536
|
-
</td>`
|
|
537
|
-
: html`<td></td>`
|
|
538
|
-
}
|
|
539
|
-
</tr>
|
|
540
|
-
<tr>
|
|
541
|
-
${this.allowTry === 'true' ? html`<td style="border:none"> </td>` : ''}
|
|
542
|
-
<td colspan="2" style="border:none">
|
|
543
|
-
<span class="m-markdown-small">${unsafeHTML(marked(param.description || ''))}</span>
|
|
544
|
-
${this.exampleListTemplate.call(this, param.name, paramSchema.type, example.exampleList)}
|
|
545
|
-
</td>
|
|
546
|
-
</tr>
|
|
547
|
-
`);
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
return html`
|
|
551
|
-
<div class="table-title top-gap">${title}</div>
|
|
552
|
-
<div style="display:block; overflow-x:auto; max-width:100%;">
|
|
553
|
-
<table role="presentation" class="m-table" style="width:100%; word-break:break-word;">
|
|
554
|
-
${tableRows}
|
|
555
|
-
</table>
|
|
556
|
-
</div>`;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
// This method is called before navigation change in focused mode
|
|
560
|
-
async beforeNavigationFocusedMode() {
|
|
561
|
-
// this.saveExampleState();
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
// This method is called after navigation change in focused mode
|
|
565
|
-
async afterNavigationFocusedMode() {
|
|
566
|
-
this.selectedRequestBodyType = '';
|
|
567
|
-
this.selectedRequestBodyExample = '';
|
|
568
|
-
this.updateExamplesFromDataAttr();
|
|
569
|
-
this.clearResponseData();
|
|
570
|
-
}
|
|
571
|
-
|
|
572
|
-
// Request-Body Event Handlers
|
|
573
|
-
onSelectExample(e) {
|
|
574
|
-
this.selectedRequestBodyExample = e.target.value;
|
|
575
|
-
const exampleDropdownEl = e.target;
|
|
576
|
-
window.setTimeout((selectEl) => {
|
|
577
|
-
const readOnlyExampleEl = selectEl.closest('.example-panel').querySelector('.request-body-param');
|
|
578
|
-
const userInputExampleTextareaEl = selectEl.closest('.example-panel').querySelector('.request-body-param-user-input');
|
|
579
|
-
userInputExampleTextareaEl.value = readOnlyExampleEl.innerText;
|
|
580
|
-
|
|
581
|
-
const requestPanelEl = this.getRequestPanel({ target: selectEl });
|
|
582
|
-
this.liveCURLSyntaxUpdate(requestPanelEl);
|
|
583
|
-
}, 0, exampleDropdownEl);
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
onMimeTypeChange(e) {
|
|
587
|
-
this.selectedRequestBodyType = e.target.value;
|
|
588
|
-
const mimeDropdownEl = e.target;
|
|
589
|
-
this.selectedRequestBodyExample = '';
|
|
590
|
-
window.setTimeout((selectEl) => {
|
|
591
|
-
const readOnlyExampleEl = selectEl.closest('.request-body-container').querySelector('.request-body-param');
|
|
592
|
-
if (readOnlyExampleEl) {
|
|
593
|
-
const userInputExampleTextareaEl = selectEl.closest('.request-body-container').querySelector('.request-body-param-user-input');
|
|
594
|
-
userInputExampleTextareaEl.value = readOnlyExampleEl.innerText;
|
|
595
|
-
}
|
|
596
|
-
}, 0, mimeDropdownEl);
|
|
597
|
-
}
|
|
598
|
-
|
|
599
|
-
requestBodyTemplate() {
|
|
600
|
-
if (!this.request_body) {
|
|
601
|
-
return '';
|
|
602
|
-
}
|
|
603
|
-
if (Object.keys(this.request_body).length === 0) {
|
|
604
|
-
return '';
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
// Variable to store partial HTMLs
|
|
608
|
-
let reqBodyTypeSelectorHtml = '';
|
|
609
|
-
let reqBodyFileInputHtml = '';
|
|
610
|
-
let reqBodyFormHtml = '';
|
|
611
|
-
let reqBodySchemaHtml = '';
|
|
612
|
-
let reqBodyExampleHtml = '';
|
|
613
|
-
|
|
614
|
-
const requestBodyTypes = [];
|
|
615
|
-
const { content } = this.request_body;
|
|
616
|
-
for (const mimeType in content) {
|
|
617
|
-
requestBodyTypes.push({
|
|
618
|
-
mimeType,
|
|
619
|
-
schema: content[mimeType].schema,
|
|
620
|
-
example: content[mimeType].example,
|
|
621
|
-
examples: content[mimeType].examples,
|
|
622
|
-
});
|
|
623
|
-
if (!this.selectedRequestBodyType) {
|
|
624
|
-
this.selectedRequestBodyType = mimeType;
|
|
625
|
-
}
|
|
626
|
-
}
|
|
627
|
-
// MIME Type selector
|
|
628
|
-
reqBodyTypeSelectorHtml = requestBodyTypes.length === 1
|
|
629
|
-
? ''
|
|
630
|
-
: html`
|
|
631
|
-
<select style="min-width:100px; max-width:100%; margin-bottom:-1px;" @change = '${(e) => this.onMimeTypeChange(e)}'>
|
|
632
|
-
${requestBodyTypes.map((reqBody) => html`
|
|
633
|
-
<option value = '${reqBody.mimeType}' ?selected = '${reqBody.mimeType === this.selectedRequestBodyType}'>
|
|
634
|
-
${reqBody.mimeType}
|
|
635
|
-
</option> `)
|
|
636
|
-
}
|
|
637
|
-
</select>
|
|
638
|
-
`;
|
|
639
|
-
|
|
640
|
-
// For Loop - Main
|
|
641
|
-
requestBodyTypes.forEach((reqBody) => {
|
|
642
|
-
let schemaAsObj;
|
|
643
|
-
let reqBodyExamples = [];
|
|
644
|
-
|
|
645
|
-
if (this.selectedRequestBodyType.includes('json') || this.selectedRequestBodyType.includes('xml') || this.selectedRequestBodyType.includes('text') || this.selectedRequestBodyType.includes('jose')) {
|
|
646
|
-
// Generate Example
|
|
647
|
-
if (reqBody.mimeType === this.selectedRequestBodyType) {
|
|
648
|
-
reqBodyExamples = generateExample(
|
|
649
|
-
reqBody.schema,
|
|
650
|
-
reqBody.mimeType,
|
|
651
|
-
standardizeExample(reqBody.examples),
|
|
652
|
-
standardizeExample(reqBody.example),
|
|
653
|
-
this.callback === 'true' || this.webhook === 'true' ? true : false, // eslint-disable-line no-unneeded-ternary
|
|
654
|
-
this.callback === 'true' || this.webhook === 'true' ? false : true, // eslint-disable-line no-unneeded-ternary
|
|
655
|
-
'text',
|
|
656
|
-
false,
|
|
657
|
-
);
|
|
658
|
-
if (!this.selectedRequestBodyExample) {
|
|
659
|
-
this.selectedRequestBodyExample = (reqBodyExamples.length > 0 ? reqBodyExamples[0].exampleId : '');
|
|
660
|
-
}
|
|
661
|
-
reqBodyExampleHtml = html`
|
|
662
|
-
${reqBodyExampleHtml}
|
|
663
|
-
<div class = 'example-panel border-top pad-top-8'>
|
|
664
|
-
${reqBodyExamples.length === 1
|
|
665
|
-
? ''
|
|
666
|
-
: html`
|
|
667
|
-
<select style="min-width:100px; max-width:100%; margin-bottom:-1px;" @change='${(e) => this.onSelectExample(e)}'>
|
|
668
|
-
${reqBodyExamples.map((v) => html`<option value="${v.exampleId}" ?selected=${v.exampleId === this.selectedRequestBodyExample} >
|
|
669
|
-
${v.exampleSummary.length > 80 ? v.exampleId : v.exampleSummary ? v.exampleSummary : v.exampleId}
|
|
670
|
-
</option>`)}
|
|
671
|
-
</select>
|
|
672
|
-
`
|
|
673
|
-
}
|
|
674
|
-
${reqBodyExamples
|
|
675
|
-
.filter((v) => v.exampleId === this.selectedRequestBodyExample)
|
|
676
|
-
.map((v) => html`
|
|
677
|
-
<div class="example ${v.exampleId === this.selectedRequestBodyExample ? 'example-selected' : ''}" data-example = '${v.exampleId}'>
|
|
678
|
-
${v.exampleSummary && v.exampleSummary.length > 80 ? html`<div style="padding: 4px 0"> ${v.exampleSummary} </div>` : ''}
|
|
679
|
-
${v.exampleDescription ? html`<div class="m-markdown-small" style="padding: 4px 0"> ${unsafeHTML(marked(v.exampleDescription || ''))} </div>` : ''}
|
|
680
|
-
<!-- This pre(hidden) is to store the original example value, this will remain unchanged when users switches from one example to another, its is used to populate the editable textarea -->
|
|
681
|
-
<pre
|
|
682
|
-
class = "textarea is-hidden request-body-param ${reqBody.mimeType.substring(reqBody.mimeType.indexOf('/') + 1)}"
|
|
683
|
-
spellcheck = "false"
|
|
684
|
-
data-ptype = "${reqBody.mimeType}"
|
|
685
|
-
style="width:100%; resize:vertical; display:none"
|
|
686
|
-
>${(v.exampleFormat === 'text' ? v.exampleValue : JSON.stringify(v.exampleValue, null, 2))}</pre>
|
|
687
|
-
|
|
688
|
-
<!-- this textarea is for user to edit the example -->
|
|
689
|
-
<textarea
|
|
690
|
-
class = "textarea request-body-param-user-input"
|
|
691
|
-
part = "textarea textarea-param"
|
|
692
|
-
spellcheck = "false"
|
|
693
|
-
data-ptype = "${reqBody.mimeType}"
|
|
694
|
-
data-example = "${v.exampleFormat === 'text' ? v.exampleValue : JSON.stringify(v.exampleValue, null, 2)}"
|
|
695
|
-
data-example-format = "${v.exampleFormat}"
|
|
696
|
-
style="width:100%; resize:vertical;"
|
|
697
|
-
.textContent = "${this.fillRequestFieldsWithExample === 'true' ? (v.exampleFormat === 'text' ? v.exampleValue : JSON.stringify(v.exampleValue, null, 2)) : ''}"
|
|
698
|
-
@input=${(e) => {
|
|
699
|
-
const requestPanelEl = this.getRequestPanel(e);
|
|
700
|
-
this.liveCURLSyntaxUpdate(requestPanelEl);
|
|
701
|
-
}}
|
|
702
|
-
@keydown=${(e) => {
|
|
703
|
-
if ((e.keyCode === 10 || e.keyCode === 13) && e.ctrlKey) {
|
|
704
|
-
return this.onTryClick(e);
|
|
705
|
-
}
|
|
706
|
-
}}
|
|
707
|
-
|
|
708
|
-
></textarea>
|
|
709
|
-
</div>
|
|
710
|
-
`)}
|
|
711
|
-
|
|
712
|
-
</div>
|
|
713
|
-
`;
|
|
714
|
-
}
|
|
715
|
-
} else if (this.selectedRequestBodyType.includes('form-urlencoded') || this.selectedRequestBodyType.includes('form-data')) {
|
|
716
|
-
if (reqBody.mimeType === this.selectedRequestBodyType) {
|
|
717
|
-
const ex = generateExample(
|
|
718
|
-
reqBody.schema,
|
|
719
|
-
reqBody.mimeType,
|
|
720
|
-
reqBody.examples,
|
|
721
|
-
reqBody.example,
|
|
722
|
-
this.callback === 'true' || this.webhook === 'true' ? true : false, // eslint-disable-line no-unneeded-ternary
|
|
723
|
-
this.callback === 'true' || this.webhook === 'true' ? false : true, // eslint-disable-line no-unneeded-ternary
|
|
724
|
-
'text',
|
|
725
|
-
false,
|
|
726
|
-
);
|
|
727
|
-
if (reqBody.schema) {
|
|
728
|
-
reqBodyFormHtml = this.formDataTemplate(reqBody.schema, reqBody.mimeType, (ex[0] ? ex[0].exampleValue : ''));
|
|
729
|
-
}
|
|
730
|
-
}
|
|
731
|
-
} else if ((/^audio\/|^image\/|^video\/|^font\/|tar$|zip$|7z$|rtf$|msword$|excel$|\/pdf$|\/octet-stream$/.test(this.selectedRequestBodyType))) {
|
|
732
|
-
if (reqBody.mimeType === this.selectedRequestBodyType) {
|
|
733
|
-
reqBodyFileInputHtml = html`
|
|
734
|
-
<div class = "small-font-size bold-text row">
|
|
735
|
-
<input id="input-request-body-param-file" type="file" part="file-input" style="max-width:100%" class="request-body-param-file" data-ptype="${reqBody.mimeType}" spellcheck="false" />
|
|
736
|
-
</div>
|
|
737
|
-
`;
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
|
|
741
|
-
// Generate Schema
|
|
742
|
-
if (reqBody.mimeType.includes('json') || reqBody.mimeType.includes('xml') || reqBody.mimeType.includes('text') || this.selectedRequestBodyType.includes('jose')) {
|
|
743
|
-
schemaAsObj = schemaInObjectNotation(reqBody.schema, {});
|
|
744
|
-
if (this.schemaStyle === 'table') {
|
|
745
|
-
reqBodySchemaHtml = html`
|
|
746
|
-
${reqBodySchemaHtml}
|
|
747
|
-
<schema-table
|
|
748
|
-
class = '${reqBody.mimeType.substring(reqBody.mimeType.indexOf('/') + 1)}'
|
|
749
|
-
style = 'display: ${this.selectedRequestBodyType === reqBody.mimeType ? 'block' : 'none'};'
|
|
750
|
-
.data = '${schemaAsObj}'
|
|
751
|
-
schema-expand-level = "${this.schemaExpandLevel}"
|
|
752
|
-
schema-description-expanded = "${this.schemaDescriptionExpanded}"
|
|
753
|
-
allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}"
|
|
754
|
-
schema-hide-read-only = "${this.schemaHideReadOnly}"
|
|
755
|
-
schema-hide-write-only = "${this.schemaHideWriteOnly}"
|
|
756
|
-
exportparts = "schema-description:schema-description, schema-multiline-toggle:schema-multiline-toggle"
|
|
757
|
-
> </schema-table>
|
|
758
|
-
`;
|
|
759
|
-
} else if (this.schemaStyle === 'tree') {
|
|
760
|
-
reqBodySchemaHtml = html`
|
|
761
|
-
${reqBodySchemaHtml}
|
|
762
|
-
<schema-tree
|
|
763
|
-
class = "${reqBody.mimeType.substring(reqBody.mimeType.indexOf('/') + 1)}"
|
|
764
|
-
style = "display: ${this.selectedRequestBodyType === reqBody.mimeType ? 'block' : 'none'};"
|
|
765
|
-
.data = "${schemaAsObj}"
|
|
766
|
-
schema-expand-level = "${this.schemaExpandLevel}"
|
|
767
|
-
schema-description-expanded = "${this.schemaDescriptionExpanded}"
|
|
768
|
-
allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}"
|
|
769
|
-
schema-hide-read-only = "${this.schemaHideReadOnly}"
|
|
770
|
-
schema-hide-write-only = "${this.schemaHideWriteOnly}"
|
|
771
|
-
exportparts = "schema-description:schema-description, schema-multiline-toggle:schema-multiline-toggle"
|
|
772
|
-
> </schema-tree>
|
|
773
|
-
`;
|
|
774
|
-
}
|
|
775
|
-
}
|
|
776
|
-
});
|
|
777
|
-
|
|
778
|
-
return html`
|
|
779
|
-
<div class='request-body-container' data-selected-request-body-type="${this.selectedRequestBodyType}">
|
|
780
|
-
<div class="table-title top-gap row">
|
|
781
|
-
REQUEST BODY ${this.request_body.required ? html`<span class="mono-font" style='color:var(--red)'>*</span>` : ''}
|
|
782
|
-
<span style = "font-weight:normal; margin-left:5px"> ${this.selectedRequestBodyType}</span>
|
|
783
|
-
<span style="flex:1"></span>
|
|
784
|
-
${reqBodyTypeSelectorHtml}
|
|
785
|
-
</div>
|
|
786
|
-
${this.request_body.description ? html`<div class="m-markdown" style="margin-bottom:12px">${unsafeHTML(marked(this.request_body.description))}</div>` : ''}
|
|
787
|
-
|
|
788
|
-
${(this.selectedRequestBodyType.includes('json') || this.selectedRequestBodyType.includes('xml') || this.selectedRequestBodyType.includes('text') || this.selectedRequestBodyType.includes('jose'))
|
|
789
|
-
? html`
|
|
790
|
-
<div class="tab-panel col" style="border-width:0 0 1px 0;">
|
|
791
|
-
<div class="tab-buttons row" @click="${(e) => { if (e.target.tagName.toLowerCase() === 'button') { this.activeSchemaTab = e.target.dataset.tab; } }}">
|
|
792
|
-
<button class="tab-btn ${this.activeSchemaTab === 'example' ? 'active' : ''}" data-tab = 'example'>EXAMPLE</button>
|
|
793
|
-
<button class="tab-btn ${this.activeSchemaTab !== 'example' ? 'active' : ''}" data-tab = 'schema'>SCHEMA</button>
|
|
794
|
-
</div>
|
|
795
|
-
${html`<div class="tab-content col" style="display:${this.activeSchemaTab === 'example' ? 'block' : 'none'};"> ${reqBodyExampleHtml}</div>`}
|
|
796
|
-
${html`<div class="tab-content col" style="display:${this.activeSchemaTab === 'example' ? 'none' : 'block'};"> ${reqBodySchemaHtml}</div>`}
|
|
797
|
-
</div>`
|
|
798
|
-
: html`
|
|
799
|
-
${reqBodyFileInputHtml}
|
|
800
|
-
${reqBodyFormHtml}`
|
|
801
|
-
}
|
|
802
|
-
</div>
|
|
803
|
-
`;
|
|
804
|
-
}
|
|
805
|
-
|
|
806
|
-
formDataParamAsObjectTemplate(fieldName, fieldSchema, mimeType) {
|
|
807
|
-
// This template is used when form-data param should be send as a object (application/json, application/xml)
|
|
808
|
-
const formdataPartSchema = schemaInObjectNotation(fieldSchema, {});
|
|
809
|
-
const formdataPartExample = generateExample(
|
|
810
|
-
fieldSchema,
|
|
811
|
-
'json',
|
|
812
|
-
standardizeExample(fieldSchema.examples),
|
|
813
|
-
standardizeExample(fieldSchema.example),
|
|
814
|
-
this.callback === 'true' || this.webhook === 'true' ? true : false, // eslint-disable-line no-unneeded-ternary
|
|
815
|
-
this.callback === 'true' || this.webhook === 'true' ? false : true, // eslint-disable-line no-unneeded-ternary
|
|
816
|
-
'text',
|
|
817
|
-
false,
|
|
818
|
-
);
|
|
819
|
-
|
|
820
|
-
return html`
|
|
821
|
-
<div class="tab-panel row" style="min-height:220px; border-left: 6px solid var(--light-border-color); align-items: stretch;">
|
|
822
|
-
<div style="width:24px; background-color:var(--light-border-color)">
|
|
823
|
-
<div class="row" style="flex-direction:row-reverse; width:160px; height:24px; transform:rotate(270deg) translateX(-160px); transform-origin:top left; display:block;" @click="${(e) => {
|
|
824
|
-
if (e.target.classList.contains('v-tab-btn')) {
|
|
825
|
-
const { tab } = e.target.dataset;
|
|
826
|
-
if (tab) {
|
|
827
|
-
const tabPanelEl = e.target.closest('.tab-panel');
|
|
828
|
-
const selectedTabBtnEl = tabPanelEl.querySelector(`.v-tab-btn[data-tab="${tab}"]`);
|
|
829
|
-
const otherTabBtnEl = [...tabPanelEl.querySelectorAll(`.v-tab-btn:not([data-tab="${tab}"])`)];
|
|
830
|
-
const selectedTabContentEl = tabPanelEl.querySelector(`.tab-content[data-tab="${tab}"]`);
|
|
831
|
-
const otherTabContentEl = [...tabPanelEl.querySelectorAll(`.tab-content:not([data-tab="${tab}"])`)];
|
|
832
|
-
selectedTabBtnEl.classList.add('active');
|
|
833
|
-
selectedTabContentEl.style.display = 'block';
|
|
834
|
-
otherTabBtnEl.forEach((el) => { el.classList.remove('active'); });
|
|
835
|
-
otherTabContentEl.forEach((el) => { el.style.display = 'none'; });
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
if (e.target.tagName.toLowerCase() === 'button') { this.activeSchemaTab = e.target.dataset.tab; }
|
|
839
|
-
}}">
|
|
840
|
-
<button class="v-tab-btn ${this.activeSchemaTab === 'example' ? 'active' : ''}" data-tab = 'example'>EXAMPLE</button>
|
|
841
|
-
<button class="v-tab-btn ${this.activeSchemaTab !== 'example' ? 'active' : ''}" data-tab = 'schema'>SCHEMA</button>
|
|
842
|
-
</div>
|
|
843
|
-
</div>
|
|
844
|
-
${html`
|
|
845
|
-
<div class="tab-content col" data-tab = 'example' style="display:${this.activeSchemaTab === 'example' ? 'block' : 'none'}; padding-left:5px; width:100%">
|
|
846
|
-
<textarea
|
|
847
|
-
class = "textarea"
|
|
848
|
-
part = "textarea textarea-param"
|
|
849
|
-
style = "width:100%; border:none; resize:vertical;"
|
|
850
|
-
data-array = "false"
|
|
851
|
-
data-ptype = "${mimeType.includes('form-urlencode') ? 'form-urlencode' : 'form-data'}"
|
|
852
|
-
data-pname = "${fieldName}"
|
|
853
|
-
data-example = "${formdataPartExample[0]?.exampleValue || ''}"
|
|
854
|
-
.textContent = "${this.fillRequestFieldsWithExample === 'true' ? formdataPartExample[0].exampleValue : ''}"
|
|
855
|
-
spellcheck = "false"
|
|
856
|
-
></textarea>
|
|
857
|
-
</div>`
|
|
858
|
-
}
|
|
859
|
-
${html`
|
|
860
|
-
<div class="tab-content col" data-tab = 'schema' style="display:${this.activeSchemaTab !== 'example' ? 'block' : 'none'}; padding-left:5px; width:100%;">
|
|
861
|
-
<schema-tree
|
|
862
|
-
.data = "${formdataPartSchema}"
|
|
863
|
-
schema-expand-level = "${this.schemaExpandLevel}"
|
|
864
|
-
schema-description-expanded = "${this.schemaDescriptionExpanded}"
|
|
865
|
-
allow-schema-description-expand-toggle = "${this.allowSchemaDescriptionExpandToggle}",
|
|
866
|
-
> </schema-tree>
|
|
867
|
-
</div>`
|
|
868
|
-
}
|
|
869
|
-
</div>
|
|
870
|
-
`;
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
formDataTemplate(schema, mimeType, exampleValue = '') {
|
|
874
|
-
const formDataTableRows = [];
|
|
875
|
-
if (schema.properties) {
|
|
876
|
-
for (const fieldName in schema.properties) {
|
|
877
|
-
const fieldSchema = schema.properties[fieldName];
|
|
878
|
-
if (fieldSchema.readOnly) {
|
|
879
|
-
continue;
|
|
880
|
-
}
|
|
881
|
-
const fieldExamples = fieldSchema.examples || fieldSchema.example || '';
|
|
882
|
-
const fieldType = fieldSchema.type;
|
|
883
|
-
const paramSchema = getTypeInfo(fieldSchema);
|
|
884
|
-
const labelColWidth = 'read focused'.includes(this.renderStyle) ? '200px' : '160px';
|
|
885
|
-
const example = normalizeExamples((paramSchema.examples || paramSchema.example), paramSchema.type);
|
|
886
|
-
formDataTableRows.push(html`
|
|
887
|
-
<tr title="${fieldSchema.deprecated ? 'Deprecated' : ''}">
|
|
888
|
-
<td style="width:${labelColWidth}; min-width:100px;">
|
|
889
|
-
<div class="param-name ${fieldSchema.deprecated ? 'deprecated' : ''}">
|
|
890
|
-
${fieldName}${(schema.required?.includes(fieldName) || fieldSchema.required) ? html`<span style='color:var(--red);'>*</span>` : ''}
|
|
891
|
-
</div>
|
|
892
|
-
<div class="param-type">${paramSchema.type}</div>
|
|
893
|
-
</td>
|
|
894
|
-
<td
|
|
895
|
-
style="${fieldType === 'object' ? 'width:100%; padding:0;' : this.allowTry === 'true' ? '' : 'display:none;'} min-width:100px;"
|
|
896
|
-
colspan="${fieldType === 'object' ? 2 : 1}">
|
|
897
|
-
${fieldType === 'array'
|
|
898
|
-
? fieldSchema.items?.format === 'binary'
|
|
899
|
-
? html`
|
|
900
|
-
<div class="file-input-container col" style='align-items:flex-end;' @click="${(e) => this.onAddRemoveFileInput(e, fieldName, mimeType)}">
|
|
901
|
-
<div class='input-set row'>
|
|
902
|
-
<input
|
|
903
|
-
type = "file"
|
|
904
|
-
part = "file-input"
|
|
905
|
-
style = "width:100%"
|
|
906
|
-
data-pname = "${fieldName}"
|
|
907
|
-
data-ptype = "${mimeType.includes('form-urlencode') ? 'form-urlencode' : 'form-data'}"
|
|
908
|
-
data-array = "false"
|
|
909
|
-
data-file-array = "true"
|
|
910
|
-
/>
|
|
911
|
-
<button class="file-input-remove-btn"> ✕ </button>
|
|
912
|
-
</div>
|
|
913
|
-
<button class="m-btn primary file-input-add-btn" part="btn btn-fill" style="margin:2px 25px 0 0; padding:2px 6px;">ADD</button>
|
|
914
|
-
</div>
|
|
915
|
-
`
|
|
916
|
-
: html`
|
|
917
|
-
<tag-input
|
|
918
|
-
style = "width:100%"
|
|
919
|
-
data-ptype = "${mimeType.includes('form-urlencode') ? 'form-urlencode' : 'form-data'}"
|
|
920
|
-
data-pname = "${fieldName}"
|
|
921
|
-
data-example = "${Array.isArray(fieldExamples) ? fieldExamples.join('~|~') : fieldExamples}"
|
|
922
|
-
data-array = "true"
|
|
923
|
-
placeholder = "add-multiple ↩"
|
|
924
|
-
.value = "${Array.isArray(fieldExamples) ? Array.isArray(fieldExamples[0]) ? fieldExamples[0] : fieldExamples : []}"
|
|
925
|
-
>
|
|
926
|
-
</tag-input>
|
|
927
|
-
`
|
|
928
|
-
: html`
|
|
929
|
-
${fieldType === 'object'
|
|
930
|
-
? this.formDataParamAsObjectTemplate.call(this, fieldName, fieldSchema, mimeType)
|
|
931
|
-
: html`
|
|
932
|
-
${this.allowTry === 'true'
|
|
933
|
-
? html`<input
|
|
934
|
-
.value = "${this.fillRequestFieldsWithExample === 'true' ? example.exampleVal : ''}"
|
|
935
|
-
spellcheck = "false"
|
|
936
|
-
type = "${fieldSchema.format === 'binary' ? 'file' : fieldSchema.format === 'password' ? 'password' : 'text'}"
|
|
937
|
-
part = "textbox textbox-param"
|
|
938
|
-
style = "width:100%"
|
|
939
|
-
data-ptype = "${mimeType.includes('form-urlencode') ? 'form-urlencode' : 'form-data'}"
|
|
940
|
-
data-pname = "${fieldName}"
|
|
941
|
-
data-example = "${Array.isArray(fieldExamples) ? fieldExamples[0] : fieldExamples}"
|
|
942
|
-
data-array = "false"
|
|
943
|
-
/>`
|
|
944
|
-
: ''
|
|
945
|
-
}
|
|
946
|
-
`
|
|
947
|
-
}`
|
|
948
|
-
}
|
|
949
|
-
</td>
|
|
950
|
-
${fieldType === 'object'
|
|
951
|
-
? ''
|
|
952
|
-
: html`
|
|
953
|
-
<td>
|
|
954
|
-
${paramSchema.default || paramSchema.constrain || paramSchema.allowedValues || paramSchema.pattern
|
|
955
|
-
? html`
|
|
956
|
-
<div class="param-constraint">
|
|
957
|
-
${paramSchema.default ? html`<span style="font-weight:bold">Default: </span>${paramSchema.default}<br/>` : ''}
|
|
958
|
-
${paramSchema.pattern ? html`<span style="font-weight:bold">Pattern: </span>${paramSchema.pattern}<br/>` : ''}
|
|
959
|
-
${paramSchema.constrain ? html`${paramSchema.constrain}<br/>` : ''}
|
|
960
|
-
${paramSchema.allowedValues && paramSchema.allowedValues.split('┃').map((v, i) => html`
|
|
961
|
-
${i > 0 ? '┃' : html`<span style="font-weight:bold">Allowed: </span>`}
|
|
962
|
-
${html`
|
|
963
|
-
<a part="anchor anchor-param-constraint" class = "${this.allowTry === 'true' ? '' : 'inactive-link'}"
|
|
964
|
-
data-type="${paramSchema.type === 'array' ? paramSchema.type : 'string'}"
|
|
965
|
-
data-enum="${v.trim()}"
|
|
966
|
-
@click="${(e) => {
|
|
967
|
-
const inputEl = e.target.closest('table').querySelector(`[data-pname="${fieldName}"]`);
|
|
968
|
-
if (inputEl) {
|
|
969
|
-
if (e.target.dataset.type === 'array') {
|
|
970
|
-
inputEl.value = [e.target.dataset.enum];
|
|
971
|
-
} else {
|
|
972
|
-
inputEl.value = e.target.dataset.enum;
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
}}"
|
|
976
|
-
>
|
|
977
|
-
${v}
|
|
978
|
-
</a>`
|
|
979
|
-
}`)
|
|
980
|
-
}
|
|
981
|
-
</div>`
|
|
982
|
-
: ''
|
|
983
|
-
}
|
|
984
|
-
</td>`
|
|
985
|
-
}
|
|
986
|
-
</tr>
|
|
987
|
-
${fieldType === 'object'
|
|
988
|
-
? ''
|
|
989
|
-
: html`
|
|
990
|
-
<tr>
|
|
991
|
-
<td style="border:none"> </td>
|
|
992
|
-
<td colspan="2" style="border:none; margin-top:0; padding:0 5px 8px 5px;">
|
|
993
|
-
<span class="m-markdown-small">${unsafeHTML(marked(fieldSchema.description || ''))}</span>
|
|
994
|
-
${this.exampleListTemplate.call(this, fieldName, paramSchema.type, example.exampleList)}
|
|
995
|
-
</td>
|
|
996
|
-
</tr>
|
|
997
|
-
`
|
|
998
|
-
}`);
|
|
999
|
-
}
|
|
1000
|
-
return html`
|
|
1001
|
-
<table role="presentation" style="width:100%;" class="m-table">
|
|
1002
|
-
${formDataTableRows}
|
|
1003
|
-
</table>
|
|
1004
|
-
`;
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
return html`
|
|
1008
|
-
<textarea
|
|
1009
|
-
class = "textarea dynamic-form-param ${mimeType}"
|
|
1010
|
-
part = "textarea textarea-param"
|
|
1011
|
-
spellcheck = "false"
|
|
1012
|
-
data-pname="dynamic-form"
|
|
1013
|
-
data-ptype="${mimeType}"
|
|
1014
|
-
.textContent = "${exampleValue}"
|
|
1015
|
-
style="width:100%"
|
|
1016
|
-
></textarea>
|
|
1017
|
-
${schema.description ? html`<span class="m-markdown-small">${unsafeHTML(marked(schema.description))}</span>` : ''}
|
|
1018
|
-
`;
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
curlSyntaxTemplate(display = 'flex') {
|
|
1022
|
-
return html`
|
|
1023
|
-
<div class="col m-markdown" style="flex:1; display:${display}; position:relative; max-width: 100%;">
|
|
1024
|
-
<button class="toolbar-btn" style = "position:absolute; top:12px; right:8px" @click='${(e) => { copyToClipboard(this.curlSyntax.trim().replace(/\\$/, ''), e); }}' part="btn btn-fill"> Copy </button>
|
|
1025
|
-
<pre style="white-space:pre"><code>${unsafeHTML(Prism.highlight(this.curlSyntax.trim().replace(/\\$/, ''), Prism.languages.shell, 'shell'))}</code></pre>
|
|
1026
|
-
</div>
|
|
1027
|
-
`;
|
|
1028
|
-
}
|
|
1029
|
-
|
|
1030
|
-
apiResponseTabTemplate() {
|
|
1031
|
-
let responseFormat = '';
|
|
1032
|
-
let responseContent = '';
|
|
1033
|
-
if (!this.responseIsBlob) {
|
|
1034
|
-
if (this.responseHeaders.includes('application/x-ndjson')) {
|
|
1035
|
-
responseFormat = 'json';
|
|
1036
|
-
const prismLines = this.responseText.split('\n').map((q) => Prism.highlight(q, Prism.languages[responseFormat], responseFormat)).join('\n');
|
|
1037
|
-
responseContent = html`<code>${unsafeHTML(prismLines)}</code>`;
|
|
1038
|
-
} else if (this.responseHeaders.includes('json')) {
|
|
1039
|
-
responseFormat = 'json';
|
|
1040
|
-
responseContent = html`<code>${unsafeHTML(Prism.highlight(this.responseText, Prism.languages[responseFormat], responseFormat))}</code>`;
|
|
1041
|
-
} else if (this.responseHeaders.includes('html') || this.responseHeaders.includes('xml')) {
|
|
1042
|
-
responseFormat = 'html';
|
|
1043
|
-
responseContent = html`<code>${unsafeHTML(Prism.highlight(this.responseText, Prism.languages[responseFormat], responseFormat))}</code>`;
|
|
1044
|
-
} else {
|
|
1045
|
-
responseFormat = 'text';
|
|
1046
|
-
responseContent = html`<code>${this.responseText}</code>`;
|
|
1047
|
-
}
|
|
1048
|
-
}
|
|
1049
|
-
return html`
|
|
1050
|
-
<div class="row" style="font-size:var(--font-size-small); margin:5px 0">
|
|
1051
|
-
<div class="response-message ${this.responseStatus}">Response Status: ${this.responseMessage}</div>
|
|
1052
|
-
<div style="flex:1"></div>
|
|
1053
|
-
<button class="m-btn" part="btn btn-outline btn-clear-response" @click="${this.clearResponseData}">CLEAR RESPONSE</button>
|
|
1054
|
-
</div>
|
|
1055
|
-
<div class="tab-panel col" style="border-width:0 0 1px 0;">
|
|
1056
|
-
<div id="tab_buttons" class="tab-buttons row" @click="${(e) => {
|
|
1057
|
-
if (e.target.classList.contains('tab-btn') === false) { return; }
|
|
1058
|
-
this.activeResponseTab = e.target.dataset.tab;
|
|
1059
|
-
}}">
|
|
1060
|
-
<button class="tab-btn ${this.activeResponseTab === 'response' ? 'active' : ''}" data-tab = 'response' > RESPONSE</button>
|
|
1061
|
-
<button class="tab-btn ${this.activeResponseTab === 'headers' ? 'active' : ''}" data-tab = 'headers' > RESPONSE HEADERS</button>
|
|
1062
|
-
${this.showCurlBeforeTry === 'true'
|
|
1063
|
-
? ''
|
|
1064
|
-
: html`<button class="tab-btn ${this.activeResponseTab === 'curl' ? 'active' : ''}" data-tab = 'curl'>CURL</button>`}
|
|
1065
|
-
</div>
|
|
1066
|
-
${this.responseIsBlob
|
|
1067
|
-
? html`
|
|
1068
|
-
<div class="tab-content col" style="flex:1; display:${this.activeResponseTab === 'response' ? 'flex' : 'none'};">
|
|
1069
|
-
${this.responseBlobType === 'image'
|
|
1070
|
-
? html`<img style="max-height:var(--resp-area-height, 400px); object-fit:contain;" class="mar-top-8" src="${ifDefined(this.responseBlobUrl)}"></img>`
|
|
1071
|
-
: ''
|
|
1072
|
-
}
|
|
1073
|
-
<button class="m-btn thin-border mar-top-8" style="width:135px" @click='${(e) => { downloadResource(this.responseBlobUrl, this.respContentDisposition, e); }}' part="btn btn-outline">
|
|
1074
|
-
DOWNLOAD
|
|
1075
|
-
</button>
|
|
1076
|
-
${this.responseBlobType === 'view' || this.responseBlobType === 'image'
|
|
1077
|
-
? html`<button class="m-btn thin-border mar-top-8" style="width:135px" @click='${(e) => { viewResource(this.responseBlobUrl, e); }}' part="btn btn-outline">VIEW (NEW TAB)</button>`
|
|
1078
|
-
: ''
|
|
1079
|
-
}
|
|
1080
|
-
</div>`
|
|
1081
|
-
: html`
|
|
1082
|
-
<div class="tab-content col m-markdown" style="flex:1; display:${this.activeResponseTab === 'response' ? 'flex' : 'none'};" >
|
|
1083
|
-
<button class="toolbar-btn" style="position:absolute; top:12px; right:8px" @click='${(e) => { copyToClipboard(this.responseText, e); }}' part="btn btn-fill"> Copy </button>
|
|
1084
|
-
<pre style="white-space:pre; min-height:50px; height:var(--resp-area-height, 400px); resize:vertical; overflow:auto">${responseContent}</pre>
|
|
1085
|
-
</div>`
|
|
1086
|
-
}
|
|
1087
|
-
<div class="tab-content col m-markdown" style="flex:1; display:${this.activeResponseTab === 'headers' ? 'flex' : 'none'};" >
|
|
1088
|
-
<button class="toolbar-btn" style = "position:absolute; top:12px; right:8px" @click='${(e) => { copyToClipboard(this.responseHeaders, e); }}' part="btn btn-fill"> Copy </button>
|
|
1089
|
-
<pre style="white-space:pre"><code>${unsafeHTML(Prism.highlight(this.responseHeaders, Prism.languages.css, 'css'))}</code></pre>
|
|
1090
|
-
</div>
|
|
1091
|
-
${this.showCurlBeforeTry === 'true' ? '' : this.curlSyntaxTemplate(this.activeResponseTab === 'curl' ? 'flex' : 'none')}
|
|
1092
|
-
</div>`;
|
|
1093
|
-
}
|
|
1094
|
-
|
|
1095
|
-
apiCallTemplate() {
|
|
1096
|
-
const selectedServerHtml = html`
|
|
1097
|
-
<div style="display:flex; flex-direction:column;">
|
|
1098
|
-
${this.serverUrl
|
|
1099
|
-
? html`
|
|
1100
|
-
<div style="display:flex; align-items:baseline;">
|
|
1101
|
-
<div style="font-weight:bold; padding-right:5px;">API Server</div>
|
|
1102
|
-
<span class = "gray-text"> ${this.serverUrl} </span>
|
|
1103
|
-
</div>
|
|
1104
|
-
`
|
|
1105
|
-
: ''
|
|
1106
|
-
}
|
|
1107
|
-
</div>
|
|
1108
|
-
`;
|
|
1109
|
-
|
|
1110
|
-
return html`
|
|
1111
|
-
<div style="display:flex; align-items:flex-end; margin:16px 0; font-size:var(--font-size-small);" part="wrap-request-btn">
|
|
1112
|
-
<div class="hide-in-small-screen" style="flex-direction:column; margin:0; width:calc(100% - 60px);">
|
|
1113
|
-
<div style="display:flex; flex-direction:row; align-items:center; overflow:hidden;">
|
|
1114
|
-
${selectedServerHtml}
|
|
1115
|
-
</div>
|
|
1116
|
-
<div style="display:flex;">
|
|
1117
|
-
<div style="font-weight:bold; padding-right:5px;">Authentication</div>
|
|
1118
|
-
${this.security?.length > 0
|
|
1119
|
-
? html`
|
|
1120
|
-
${this.api_keys.length > 0
|
|
1121
|
-
? html`<div style="color:var(--blue); overflow:hidden;">
|
|
1122
|
-
${this.api_keys.length === 1
|
|
1123
|
-
? `${this.api_keys[0]?.typeDisplay} in ${this.api_keys[0].in}`
|
|
1124
|
-
: `${this.api_keys.length} API keys applied`
|
|
1125
|
-
}
|
|
1126
|
-
</div>`
|
|
1127
|
-
: html`<div class="gray-text">Required <span style="color:var(--red)">(None Applied)</span> </div>`
|
|
1128
|
-
}`
|
|
1129
|
-
: html`<span class="gray-text"> Not Required </span>`
|
|
1130
|
-
}
|
|
1131
|
-
</div>
|
|
1132
|
-
</div>
|
|
1133
|
-
${
|
|
1134
|
-
this.parameters.length > 0 || this.request_body
|
|
1135
|
-
? html`
|
|
1136
|
-
<button class="m-btn thin-border" part="btn btn-outline btn-fill" style="margin-right:5px;" @click="${this.onFillRequestData}" title="Fills with example data (if provided)">
|
|
1137
|
-
FILL EXAMPLE
|
|
1138
|
-
</button>
|
|
1139
|
-
<button class="m-btn thin-border" part="btn btn-outline btn-clear" style="margin-right:5px;" @click="${this.onClearRequestData}">
|
|
1140
|
-
CLEAR
|
|
1141
|
-
</button>`
|
|
1142
|
-
: ''
|
|
1143
|
-
}
|
|
1144
|
-
<button class="m-btn primary thin-border" part="btn btn-try" @click="${this.onTryClick}">TRY</button>
|
|
1145
|
-
</div>
|
|
1146
|
-
<div class="row" style="font-size:var(--font-size-small); margin:5px 0">
|
|
1147
|
-
${this.showCurlBeforeTry === 'true' ? this.curlSyntaxTemplate() : ''}
|
|
1148
|
-
</div>
|
|
1149
|
-
${this.responseMessage === '' ? '' : this.apiResponseTabTemplate()}
|
|
1150
|
-
`;
|
|
1151
|
-
}
|
|
1152
|
-
/* eslint-enable indent */
|
|
1153
|
-
|
|
1154
|
-
async onFillRequestData(e) {
|
|
1155
|
-
const requestPanelEl = e.target.closest('.request-panel');
|
|
1156
|
-
const requestPanelInputEls = [...requestPanelEl.querySelectorAll('input, tag-input, textarea:not(.is-hidden)')];
|
|
1157
|
-
requestPanelInputEls.forEach((el) => {
|
|
1158
|
-
if (el.dataset.example) {
|
|
1159
|
-
if (el.tagName.toUpperCase() === 'TAG-INPUT') {
|
|
1160
|
-
el.value = el.dataset.example.split('~|~');
|
|
1161
|
-
} else {
|
|
1162
|
-
el.value = el.dataset.example;
|
|
1163
|
-
}
|
|
1164
|
-
}
|
|
1165
|
-
});
|
|
1166
|
-
}
|
|
1167
|
-
|
|
1168
|
-
async onClearRequestData(e) {
|
|
1169
|
-
const requestPanelEl = e.target.closest('.request-panel');
|
|
1170
|
-
const requestPanelInputEls = [...requestPanelEl.querySelectorAll('input, tag-input, textarea:not(.is-hidden)')];
|
|
1171
|
-
requestPanelInputEls.forEach((el) => { el.value = ''; });
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
buildFetchURL(requestPanelEl) {
|
|
1175
|
-
let fetchUrl;
|
|
1176
|
-
const pathParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='path']")];
|
|
1177
|
-
const queryParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='query']")];
|
|
1178
|
-
const queryParamObjTypeEls = [...requestPanelEl.querySelectorAll("[data-ptype='query-object']")];
|
|
1179
|
-
fetchUrl = this.path;
|
|
1180
|
-
// Generate URL using Path Params
|
|
1181
|
-
pathParamEls.map((el) => {
|
|
1182
|
-
fetchUrl = fetchUrl.replace(`{${el.dataset.pname}}`, encodeURIComponent(el.value));
|
|
1183
|
-
});
|
|
1184
|
-
|
|
1185
|
-
// Query Params
|
|
1186
|
-
const urlQueryParamsMap = new Map();
|
|
1187
|
-
const queryParamsWithReservedCharsAllowed = [];
|
|
1188
|
-
if (queryParamEls.length > 0) {
|
|
1189
|
-
queryParamEls.forEach((el) => {
|
|
1190
|
-
const queryParam = new URLSearchParams();
|
|
1191
|
-
if (el.dataset.paramAllowReserved === 'true') {
|
|
1192
|
-
queryParamsWithReservedCharsAllowed.push(el.dataset.pname);
|
|
1193
|
-
}
|
|
1194
|
-
if (el.dataset.array === 'false') {
|
|
1195
|
-
if (el.value !== '') {
|
|
1196
|
-
queryParam.append(el.dataset.pname, el.value);
|
|
1197
|
-
}
|
|
1198
|
-
} else {
|
|
1199
|
-
const { paramSerializeStyle, paramSerializeExplode } = el.dataset;
|
|
1200
|
-
let vals = ((el.value && Array.isArray(el.value)) ? el.value : []);
|
|
1201
|
-
vals = Array.isArray(vals) ? vals.filter((v) => v !== '') : [];
|
|
1202
|
-
if (vals.length > 0) {
|
|
1203
|
-
if (paramSerializeStyle === 'spaceDelimited') {
|
|
1204
|
-
queryParam.append(el.dataset.pname, vals.join(' ').replace(/^\s|\s$/g, ''));
|
|
1205
|
-
} else if (paramSerializeStyle === 'pipeDelimited') {
|
|
1206
|
-
queryParam.append(el.dataset.pname, vals.join('|').replace(/^\||\|$/g, ''));
|
|
1207
|
-
} else {
|
|
1208
|
-
if (paramSerializeExplode === 'true') { // eslint-disable-line no-lonely-if
|
|
1209
|
-
vals.forEach((v) => { queryParam.append(el.dataset.pname, v); });
|
|
1210
|
-
} else {
|
|
1211
|
-
queryParam.append(el.dataset.pname, vals.join(',').replace(/^,|,$/g, ''));
|
|
1212
|
-
}
|
|
1213
|
-
}
|
|
1214
|
-
}
|
|
1215
|
-
}
|
|
1216
|
-
if (queryParam.toString()) {
|
|
1217
|
-
urlQueryParamsMap.set(el.dataset.pname, queryParam);
|
|
1218
|
-
}
|
|
1219
|
-
});
|
|
1220
|
-
}
|
|
1221
|
-
|
|
1222
|
-
// Query Params (Dynamic - create from JSON)
|
|
1223
|
-
if (queryParamObjTypeEls.length > 0) {
|
|
1224
|
-
queryParamObjTypeEls.map((el) => {
|
|
1225
|
-
const queryParam = new URLSearchParams();
|
|
1226
|
-
try {
|
|
1227
|
-
let queryParamObj = {};
|
|
1228
|
-
const { paramSerializeStyle, paramSerializeExplode, pname } = el.dataset;
|
|
1229
|
-
queryParamObj = Object.assign(queryParamObj, JSON.parse(el.value.replace(/\s+/g, ' ')));
|
|
1230
|
-
if (el.dataset.paramAllowReserved === 'true') {
|
|
1231
|
-
queryParamsWithReservedCharsAllowed.push(el.dataset.pname);
|
|
1232
|
-
}
|
|
1233
|
-
if ('json xml'.includes(paramSerializeStyle)) {
|
|
1234
|
-
if (paramSerializeStyle === 'json') {
|
|
1235
|
-
queryParam.append(el.dataset.pname, JSON.stringify(queryParamObj));
|
|
1236
|
-
} else if (paramSerializeStyle === 'xml') {
|
|
1237
|
-
queryParam.append(el.dataset.pname, json2xml(queryParamObj));
|
|
1238
|
-
}
|
|
1239
|
-
} else {
|
|
1240
|
-
for (const key in queryParamObj) {
|
|
1241
|
-
const pKey = `${pname}[${key}]`;
|
|
1242
|
-
if (typeof queryParamObj[key] === 'object') {
|
|
1243
|
-
if (Array.isArray(queryParamObj[key])) {
|
|
1244
|
-
if (paramSerializeStyle === 'spaceDelimited') {
|
|
1245
|
-
queryParam.append(pKey, queryParamObj[key].join(' '));
|
|
1246
|
-
} else if (paramSerializeStyle === 'pipeDelimited') {
|
|
1247
|
-
queryParam.append(pKey, queryParamObj[key].join('|'));
|
|
1248
|
-
} else {
|
|
1249
|
-
if (paramSerializeExplode === 'true') { // eslint-disable-line no-lonely-if
|
|
1250
|
-
queryParamObj[key].forEach((v) => {
|
|
1251
|
-
queryParam.append(pKey, v);
|
|
1252
|
-
});
|
|
1253
|
-
} else {
|
|
1254
|
-
queryParam.append(pKey, queryParamObj[key]);
|
|
1255
|
-
}
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
} else {
|
|
1259
|
-
queryParam.append(pKey, queryParamObj[key]);
|
|
1260
|
-
}
|
|
1261
|
-
}
|
|
1262
|
-
}
|
|
1263
|
-
} catch {
|
|
1264
|
-
console.error('RapiDoc: unable to parse %s into object', el.value); // eslint-disable-line no-console
|
|
1265
|
-
}
|
|
1266
|
-
if (queryParam.toString()) {
|
|
1267
|
-
urlQueryParamsMap.set(el.dataset.pname, queryParam);
|
|
1268
|
-
}
|
|
1269
|
-
});
|
|
1270
|
-
}
|
|
1271
|
-
let urlQueryParamString = '';
|
|
1272
|
-
if (urlQueryParamsMap.size) {
|
|
1273
|
-
urlQueryParamsMap.forEach((val, pname) => {
|
|
1274
|
-
if (queryParamsWithReservedCharsAllowed.includes(pname)) {
|
|
1275
|
-
urlQueryParamString += `${pname}=`;
|
|
1276
|
-
urlQueryParamString += val.getAll(pname).join(`&${pname}=`);
|
|
1277
|
-
urlQueryParamString += '&';
|
|
1278
|
-
} else {
|
|
1279
|
-
urlQueryParamString += `${val.toString()}&`;
|
|
1280
|
-
}
|
|
1281
|
-
});
|
|
1282
|
-
urlQueryParamString = urlQueryParamString.slice(0, -1);
|
|
1283
|
-
}
|
|
1284
|
-
if (urlQueryParamString.length !== 0) {
|
|
1285
|
-
fetchUrl = `${fetchUrl}${fetchUrl.includes('?') ? '&' : '?'}${urlQueryParamString}`;
|
|
1286
|
-
}
|
|
1287
|
-
|
|
1288
|
-
// Add authentication Query-Param if provided
|
|
1289
|
-
this.api_keys
|
|
1290
|
-
.filter((v) => (v.in === 'query'))
|
|
1291
|
-
.forEach((v) => {
|
|
1292
|
-
fetchUrl = `${fetchUrl}${fetchUrl.includes('?') ? '&' : '?'}${v.name}=${encodeURIComponent(v.finalKeyValue)}`;
|
|
1293
|
-
});
|
|
1294
|
-
|
|
1295
|
-
fetchUrl = `${this.serverUrl.replace(/\/$/, '')}${fetchUrl}`;
|
|
1296
|
-
return fetchUrl;
|
|
1297
|
-
}
|
|
1298
|
-
|
|
1299
|
-
buildFetchHeaders(requestPanelEl) {
|
|
1300
|
-
const respEl = this.closest('.expanded-req-resp-container, .req-resp-container')?.getElementsByTagName('api-response')[0];
|
|
1301
|
-
const headerParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='header'], [data-ptype='header-object']")];
|
|
1302
|
-
const requestBodyContainerEl = requestPanelEl.querySelector('.request-body-container');
|
|
1303
|
-
const acceptHeader = respEl?.selectedMimeType;
|
|
1304
|
-
const reqHeaders = new Headers();
|
|
1305
|
-
if (acceptHeader) {
|
|
1306
|
-
// Uses the acceptHeader from Response panel
|
|
1307
|
-
reqHeaders.append('Accept', acceptHeader);
|
|
1308
|
-
} else if (this.accept) {
|
|
1309
|
-
reqHeaders.append('Accept', this.accept);
|
|
1310
|
-
}
|
|
1311
|
-
|
|
1312
|
-
// Add Authentication Header if provided
|
|
1313
|
-
this.api_keys
|
|
1314
|
-
.filter((v) => (v.in === 'header'))
|
|
1315
|
-
.forEach((v) => {
|
|
1316
|
-
reqHeaders.append(v.name, v.finalKeyValue);
|
|
1317
|
-
});
|
|
1318
|
-
|
|
1319
|
-
// Add Header Params
|
|
1320
|
-
headerParamEls.map((el) => {
|
|
1321
|
-
if (el.value) {
|
|
1322
|
-
if (el.dataset.ptype === 'header-object') {
|
|
1323
|
-
/* CONVERT
|
|
1324
|
-
a header value from below object style
|
|
1325
|
-
{
|
|
1326
|
-
"key1": "val1",
|
|
1327
|
-
"key2": {
|
|
1328
|
-
"key2_1": "val2_1",
|
|
1329
|
-
"key2_2": {
|
|
1330
|
-
"key2_2_1": "val2_2_1"
|
|
1331
|
-
}
|
|
1332
|
-
},
|
|
1333
|
-
"key3": "val3"
|
|
1334
|
-
};
|
|
1335
|
-
|
|
1336
|
-
TO >>>
|
|
1337
|
-
key1=val1, key2={"key2_1":"val2_1","key2_2":{"key2_2_1":"val2_2_1"}}, key3=val3
|
|
1338
|
-
*/
|
|
1339
|
-
const headerObjVal = JSON.parse(el.value.replace(/\n/g, '').trim());
|
|
1340
|
-
const firstLevelKeySeparator = el.dataset.paramSerializeExplode === 'true' ? '=' : ',';
|
|
1341
|
-
const headerStrVal = Object.keys(headerObjVal)
|
|
1342
|
-
.map((key) => {
|
|
1343
|
-
const value = headerObjVal[key];
|
|
1344
|
-
if (typeof value === 'object') {
|
|
1345
|
-
return `${key}${firstLevelKeySeparator}${JSON.stringify(value)}`;
|
|
1346
|
-
}
|
|
1347
|
-
return `${key}${firstLevelKeySeparator}${value}`;
|
|
1348
|
-
})
|
|
1349
|
-
.join(',');
|
|
1350
|
-
reqHeaders.append(el.dataset.pname, headerStrVal);
|
|
1351
|
-
} else {
|
|
1352
|
-
reqHeaders.append(el.dataset.pname, el.value);
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
|
-
});
|
|
1356
|
-
|
|
1357
|
-
if (requestBodyContainerEl) {
|
|
1358
|
-
const requestBodyType = requestBodyContainerEl.dataset.selectedRequestBodyType;
|
|
1359
|
-
// Common for all request-body
|
|
1360
|
-
if (!requestBodyType.includes('form-data')) {
|
|
1361
|
-
// For multipart/form-data dont set the content-type to allow creation of browser generated part boundaries
|
|
1362
|
-
reqHeaders.append('Content-Type', requestBodyType);
|
|
1363
|
-
}
|
|
1364
|
-
}
|
|
1365
|
-
return reqHeaders;
|
|
1366
|
-
}
|
|
1367
|
-
|
|
1368
|
-
buildFetchBodyOptions(requestPanelEl) {
|
|
1369
|
-
const requestBodyContainerEl = requestPanelEl.querySelector('.request-body-container');
|
|
1370
|
-
const fetchOptions = {
|
|
1371
|
-
method: this.method.toUpperCase(),
|
|
1372
|
-
};
|
|
1373
|
-
if (requestBodyContainerEl) {
|
|
1374
|
-
const requestBodyType = requestBodyContainerEl.dataset.selectedRequestBodyType;
|
|
1375
|
-
if (requestBodyType.includes('form-urlencoded')) {
|
|
1376
|
-
// url-encoded Form Params (dynamic) - Parse JSON and generate Params
|
|
1377
|
-
const formUrlDynamicTextAreaEl = requestPanelEl.querySelector("[data-ptype='dynamic-form']");
|
|
1378
|
-
if (formUrlDynamicTextAreaEl) {
|
|
1379
|
-
const val = formUrlDynamicTextAreaEl.value;
|
|
1380
|
-
const formUrlDynParams = new URLSearchParams();
|
|
1381
|
-
let proceed = true;
|
|
1382
|
-
let tmpObj;
|
|
1383
|
-
if (val) {
|
|
1384
|
-
try {
|
|
1385
|
-
tmpObj = JSON.parse(val);
|
|
1386
|
-
} catch (err) {
|
|
1387
|
-
proceed = false;
|
|
1388
|
-
console.warn('RapiDoc: Invalid JSON provided', err); // eslint-disable-line no-console
|
|
1389
|
-
}
|
|
1390
|
-
} else {
|
|
1391
|
-
proceed = false;
|
|
1392
|
-
}
|
|
1393
|
-
if (proceed) {
|
|
1394
|
-
for (const prop in tmpObj) {
|
|
1395
|
-
formUrlDynParams.append(prop, JSON.stringify(tmpObj[prop]));
|
|
1396
|
-
}
|
|
1397
|
-
fetchOptions.body = formUrlDynParams;
|
|
1398
|
-
}
|
|
1399
|
-
} else {
|
|
1400
|
-
// url-encoded Form Params (regular)
|
|
1401
|
-
const formUrlEls = [...requestPanelEl.querySelectorAll("[data-ptype='form-urlencode']")];
|
|
1402
|
-
const formUrlParams = new URLSearchParams();
|
|
1403
|
-
formUrlEls
|
|
1404
|
-
.filter((v) => (v.type !== 'file'))
|
|
1405
|
-
.forEach((el) => {
|
|
1406
|
-
if (el.dataset.array === 'false') {
|
|
1407
|
-
if (el.value) {
|
|
1408
|
-
formUrlParams.append(el.dataset.pname, el.value);
|
|
1409
|
-
}
|
|
1410
|
-
} else {
|
|
1411
|
-
const vals = (el.value && Array.isArray(el.value)) ? el.value.join(',') : '';
|
|
1412
|
-
formUrlParams.append(el.dataset.pname, vals);
|
|
1413
|
-
}
|
|
1414
|
-
});
|
|
1415
|
-
fetchOptions.body = formUrlParams;
|
|
1416
|
-
}
|
|
1417
|
-
} else if (requestBodyType.includes('form-data')) {
|
|
1418
|
-
const formDataParams = new FormData();
|
|
1419
|
-
const formDataEls = [...requestPanelEl.querySelectorAll("[data-ptype='form-data']")];
|
|
1420
|
-
formDataEls.forEach((el) => {
|
|
1421
|
-
if (el.dataset.array === 'false') {
|
|
1422
|
-
if (el.type === 'file' && el.files[0]) {
|
|
1423
|
-
formDataParams.append(el.dataset.pname, el.files[0], el.files[0].name);
|
|
1424
|
-
} else if (el.value) {
|
|
1425
|
-
formDataParams.append(el.dataset.pname, el.value);
|
|
1426
|
-
}
|
|
1427
|
-
} else if (el.value && Array.isArray(el.value)) {
|
|
1428
|
-
formDataParams.append(el.dataset.pname, el.value.join(','));
|
|
1429
|
-
}
|
|
1430
|
-
});
|
|
1431
|
-
fetchOptions.body = formDataParams;
|
|
1432
|
-
} else if (/^audio\/|^image\/|^video\/|^font\/|tar$|zip$|7z$|rtf$|msword$|excel$|\/pdf$|\/octet-stream$/.test(requestBodyType)) {
|
|
1433
|
-
const bodyParamFileEl = requestPanelEl.querySelector('.request-body-param-file');
|
|
1434
|
-
if (bodyParamFileEl?.files[0]) {
|
|
1435
|
-
fetchOptions.body = bodyParamFileEl.files[0]; // eslint-disable-line prefer-destructuring
|
|
1436
|
-
}
|
|
1437
|
-
} else if (requestBodyType.includes('json') || requestBodyType.includes('xml') || requestBodyType.includes('text')) {
|
|
1438
|
-
const exampleTextAreaEl = requestPanelEl.querySelector('.request-body-param-user-input');
|
|
1439
|
-
if (exampleTextAreaEl?.value) {
|
|
1440
|
-
fetchOptions.body = exampleTextAreaEl.value;
|
|
1441
|
-
}
|
|
1442
|
-
}
|
|
1443
|
-
}
|
|
1444
|
-
|
|
1445
|
-
return fetchOptions;
|
|
1446
|
-
}
|
|
1447
|
-
|
|
1448
|
-
async onTryClick(e) {
|
|
1449
|
-
const tryBtnEl = e.target;
|
|
1450
|
-
const requestPanelEl = tryBtnEl.closest('.request-panel');
|
|
1451
|
-
const fetchUrl = this.buildFetchURL(requestPanelEl);
|
|
1452
|
-
const fetchOptions = this.buildFetchBodyOptions(requestPanelEl);
|
|
1453
|
-
const reqHeaders = this.buildFetchHeaders(requestPanelEl);
|
|
1454
|
-
this.responseUrl = '';
|
|
1455
|
-
this.responseHeaders = [];
|
|
1456
|
-
this.curlSyntax = this.generateCURLSyntax(fetchUrl, reqHeaders, fetchOptions, requestPanelEl);
|
|
1457
|
-
this.responseStatus = 'success';
|
|
1458
|
-
this.responseIsBlob = false;
|
|
1459
|
-
|
|
1460
|
-
this.respContentDisposition = '';
|
|
1461
|
-
if (this.responseBlobUrl) {
|
|
1462
|
-
URL.revokeObjectURL(this.responseBlobUrl);
|
|
1463
|
-
this.responseBlobUrl = '';
|
|
1464
|
-
}
|
|
1465
|
-
if (this.fetchCredentials) {
|
|
1466
|
-
fetchOptions.credentials = this.fetchCredentials;
|
|
1467
|
-
}
|
|
1468
|
-
const controller = new AbortController();
|
|
1469
|
-
const { signal } = controller;
|
|
1470
|
-
fetchOptions.headers = reqHeaders;
|
|
1471
|
-
const tempRequest = { url: fetchUrl, ...fetchOptions };
|
|
1472
|
-
this.dispatchEvent(new CustomEvent('before-try', {
|
|
1473
|
-
bubbles: true,
|
|
1474
|
-
composed: true,
|
|
1475
|
-
detail: {
|
|
1476
|
-
request: tempRequest,
|
|
1477
|
-
controller,
|
|
1478
|
-
},
|
|
1479
|
-
}));
|
|
1480
|
-
const updatedFetchOptions = {
|
|
1481
|
-
method: tempRequest.method,
|
|
1482
|
-
headers: tempRequest.headers,
|
|
1483
|
-
credentials: tempRequest.credentials,
|
|
1484
|
-
body: tempRequest.body,
|
|
1485
|
-
};
|
|
1486
|
-
const fetchRequest = new Request(tempRequest.url, updatedFetchOptions);
|
|
1487
|
-
|
|
1488
|
-
let fetchResponse;
|
|
1489
|
-
let responseClone;
|
|
1490
|
-
try {
|
|
1491
|
-
let respBlob;
|
|
1492
|
-
let respJson;
|
|
1493
|
-
let respText;
|
|
1494
|
-
tryBtnEl.disabled = true;
|
|
1495
|
-
this.responseText = '⌛';
|
|
1496
|
-
this.responseMessage = '';
|
|
1497
|
-
this.requestUpdate();
|
|
1498
|
-
const startTime = performance.now();
|
|
1499
|
-
fetchResponse = await fetch(fetchRequest, { signal });
|
|
1500
|
-
const endTime = performance.now();
|
|
1501
|
-
// Allow to modify response
|
|
1502
|
-
/*
|
|
1503
|
-
let resolveModifiedResponse; // Create a promise that will be resolved from the event listener
|
|
1504
|
-
const modifiedResponsePromise = new Promise((resolve) => {
|
|
1505
|
-
resolveModifiedResponse = resolve;
|
|
1506
|
-
});
|
|
1507
|
-
this.dispatchEvent(new CustomEvent('fetched-try', {
|
|
1508
|
-
bubbles: true,
|
|
1509
|
-
composed: true,
|
|
1510
|
-
detail: {
|
|
1511
|
-
request: fetchRequest,
|
|
1512
|
-
response: fetchResponse,
|
|
1513
|
-
resolveModifiedResponse, // pass the resolver function
|
|
1514
|
-
},
|
|
1515
|
-
}));
|
|
1516
|
-
fetchResponse = await modifiedResponsePromise; // Wait for the modified response
|
|
1517
|
-
*/
|
|
1518
|
-
responseClone = fetchResponse.clone(); // create a response clone to allow reading response body again (response.json, response.text etc)
|
|
1519
|
-
tryBtnEl.disabled = false;
|
|
1520
|
-
this.responseMessage = html`${fetchResponse.statusText ? `${fetchResponse.statusText}:${fetchResponse.status}` : fetchResponse.status} <div style="color:var(--light-fg)"> Took ${Math.round(endTime - startTime)} milliseconds </div>`;
|
|
1521
|
-
this.responseUrl = fetchResponse.url;
|
|
1522
|
-
const respHeadersObj = {};
|
|
1523
|
-
fetchResponse.headers.forEach((hdrVal, hdr) => {
|
|
1524
|
-
respHeadersObj[hdr] = hdrVal;
|
|
1525
|
-
this.responseHeaders = `${this.responseHeaders}${hdr}: ${hdrVal}\n`;
|
|
1526
|
-
});
|
|
1527
|
-
const contentType = fetchResponse.headers.get('content-type').split(';')[0].trim();
|
|
1528
|
-
const respEmpty = (await fetchResponse.clone().text()).length === 0;
|
|
1529
|
-
if (respEmpty) {
|
|
1530
|
-
this.responseText = '';
|
|
1531
|
-
} else if (contentType) {
|
|
1532
|
-
if (contentType === 'application/x-ndjson') {
|
|
1533
|
-
this.responseText = await fetchResponse.text();
|
|
1534
|
-
} else if (contentType.includes('json')) {
|
|
1535
|
-
if ((/charset=[^"']+/).test(contentType)) {
|
|
1536
|
-
const encoding = contentType.split('charset=')[1];
|
|
1537
|
-
const buffer = await fetchResponse.arrayBuffer();
|
|
1538
|
-
try {
|
|
1539
|
-
respText = new TextDecoder(encoding).decode(buffer);
|
|
1540
|
-
} catch {
|
|
1541
|
-
respText = new TextDecoder('utf-8').decode(buffer);
|
|
1542
|
-
}
|
|
1543
|
-
try {
|
|
1544
|
-
respJson = JSON.parse(respText);
|
|
1545
|
-
this.responseText = JSON.stringify(respJson, null, 2);
|
|
1546
|
-
} catch {
|
|
1547
|
-
this.responseText = respText;
|
|
1548
|
-
}
|
|
1549
|
-
} else {
|
|
1550
|
-
respJson = await fetchResponse.json();
|
|
1551
|
-
this.responseText = JSON.stringify(respJson, null, 2);
|
|
1552
|
-
}
|
|
1553
|
-
// eslint-disable-next-line no-useless-escape
|
|
1554
|
-
} else if (/^font\/|tar$|zip$|7z$|rtf$|msword$|excel$|\/pdf$|\/octet-stream$|^application\/vnd\./.test(contentType)) {
|
|
1555
|
-
this.responseIsBlob = true;
|
|
1556
|
-
this.responseBlobType = 'download';
|
|
1557
|
-
} else if (/^image/.test(contentType)) {
|
|
1558
|
-
this.responseIsBlob = true;
|
|
1559
|
-
this.responseBlobType = 'image';
|
|
1560
|
-
} else if (/^audio|^image|^video/.test(contentType)) {
|
|
1561
|
-
this.responseIsBlob = true;
|
|
1562
|
-
this.responseBlobType = 'view';
|
|
1563
|
-
} else {
|
|
1564
|
-
respText = await fetchResponse.text();
|
|
1565
|
-
if (contentType.includes('xml')) {
|
|
1566
|
-
this.responseText = formatXml(respText, { textNodesOnSameLine: true, indentor: ' ' });
|
|
1567
|
-
} else {
|
|
1568
|
-
this.responseText = respText;
|
|
1569
|
-
}
|
|
1570
|
-
}
|
|
1571
|
-
if (this.responseIsBlob) {
|
|
1572
|
-
const contentDisposition = fetchResponse.headers.get('content-disposition');
|
|
1573
|
-
let filenameFromContentDeposition = 'filename';
|
|
1574
|
-
const filenameStarRegexMatch = contentDisposition.match(/filename\*=\s*UTF-8''([^;]+)/); // Support Headers like >>> Content-Disposition: attachment; filename*=UTF-8''example%20file.pdf
|
|
1575
|
-
if (filenameStarRegexMatch) {
|
|
1576
|
-
filenameFromContentDeposition = decodeURIComponent(filenameStarRegexMatch[1]); // the filename* format in the Content-Disposition header follows RFC 5987, which allows encoding non-ASCII characters using percent encoding. so example%20file.pdf becomes example file.pdf
|
|
1577
|
-
} else {
|
|
1578
|
-
// Fallback to the regular filename format
|
|
1579
|
-
const filenameMatch = contentDisposition.match(/filename="?([^"]+)"?/); // Content-Disposition: attachment; filename=example.pdf
|
|
1580
|
-
if (filenameMatch) {
|
|
1581
|
-
filenameFromContentDeposition = filenameMatch[1];
|
|
1582
|
-
}
|
|
1583
|
-
}
|
|
1584
|
-
this.respContentDisposition = filenameFromContentDeposition;
|
|
1585
|
-
respBlob = await fetchResponse.blob();
|
|
1586
|
-
this.responseBlobUrl = URL.createObjectURL(respBlob);
|
|
1587
|
-
}
|
|
1588
|
-
} else {
|
|
1589
|
-
respText = await fetchResponse.text();
|
|
1590
|
-
this.responseText = respText;
|
|
1591
|
-
}
|
|
1592
|
-
this.dispatchEvent(new CustomEvent('after-try', {
|
|
1593
|
-
bubbles: true,
|
|
1594
|
-
composed: true,
|
|
1595
|
-
detail: {
|
|
1596
|
-
request: fetchRequest,
|
|
1597
|
-
response: responseClone,
|
|
1598
|
-
responseHeaders: respHeadersObj,
|
|
1599
|
-
responseBody: respJson || respText || respBlob,
|
|
1600
|
-
responseStatus: responseClone.ok,
|
|
1601
|
-
},
|
|
1602
|
-
}));
|
|
1603
|
-
} catch (err) {
|
|
1604
|
-
tryBtnEl.disabled = false;
|
|
1605
|
-
if (err.name === 'AbortError') {
|
|
1606
|
-
this.dispatchEvent(new CustomEvent('request-aborted', {
|
|
1607
|
-
bubbles: true,
|
|
1608
|
-
composed: true,
|
|
1609
|
-
detail: {
|
|
1610
|
-
err,
|
|
1611
|
-
request: fetchRequest,
|
|
1612
|
-
},
|
|
1613
|
-
}));
|
|
1614
|
-
this.responseMessage = 'Request Aborted';
|
|
1615
|
-
this.responseText = 'Request Aborted';
|
|
1616
|
-
} else {
|
|
1617
|
-
this.dispatchEvent(new CustomEvent('after-try', {
|
|
1618
|
-
bubbles: true,
|
|
1619
|
-
composed: true,
|
|
1620
|
-
detail: {
|
|
1621
|
-
err,
|
|
1622
|
-
request: fetchRequest,
|
|
1623
|
-
},
|
|
1624
|
-
}));
|
|
1625
|
-
this.responseMessage = `${err.message} (CORS or Network Issue)`;
|
|
1626
|
-
}
|
|
1627
|
-
}
|
|
1628
|
-
this.requestUpdate();
|
|
1629
|
-
}
|
|
1630
|
-
|
|
1631
|
-
liveCURLSyntaxUpdate(requestPanelEl) {
|
|
1632
|
-
this.applyCURLSyntax(requestPanelEl);
|
|
1633
|
-
this.requestUpdate();
|
|
1634
|
-
}
|
|
1635
|
-
|
|
1636
|
-
onGenerateCURLClick(e) {
|
|
1637
|
-
const requestPanelEl = this.getRequestPanel(e);
|
|
1638
|
-
this.applyCURLSyntax(requestPanelEl);
|
|
1639
|
-
}
|
|
1640
|
-
|
|
1641
|
-
getRequestPanel(e) {
|
|
1642
|
-
return e.target.closest('.request-panel');
|
|
1643
|
-
}
|
|
1644
|
-
|
|
1645
|
-
applyCURLSyntax(requestPanelEl) {
|
|
1646
|
-
const fetchUrl = this.buildFetchURL(requestPanelEl);
|
|
1647
|
-
const fetchOptions = this.buildFetchBodyOptions(requestPanelEl);
|
|
1648
|
-
const fetchHeaders = this.buildFetchHeaders(requestPanelEl);
|
|
1649
|
-
|
|
1650
|
-
this.curlSyntax = this.generateCURLSyntax(fetchUrl, fetchHeaders, fetchOptions, requestPanelEl);
|
|
1651
|
-
}
|
|
1652
|
-
|
|
1653
|
-
generateCURLSyntax(fetchUrl, fetchHeaders, fetchOptions, requestPanelEl) {
|
|
1654
|
-
let curlUrl;
|
|
1655
|
-
let curl = '';
|
|
1656
|
-
let curlHeaders = '';
|
|
1657
|
-
let curlData = '';
|
|
1658
|
-
let curlForm = '';
|
|
1659
|
-
const requestBodyContainerEl = requestPanelEl.querySelector('.request-body-container');
|
|
1660
|
-
|
|
1661
|
-
if (fetchUrl.startsWith('http') === false) {
|
|
1662
|
-
const url = new URL(fetchUrl, window.location.href);
|
|
1663
|
-
curlUrl = url.href;
|
|
1664
|
-
} else {
|
|
1665
|
-
curlUrl = fetchUrl;
|
|
1666
|
-
}
|
|
1667
|
-
|
|
1668
|
-
curl = `curl -X ${this.method.toUpperCase()} "${curlUrl}" \\\n`;
|
|
1669
|
-
|
|
1670
|
-
fetchHeaders.forEach((value, key) => {
|
|
1671
|
-
let tempHeaderArray = value.split(',');
|
|
1672
|
-
tempHeaderArray = tempHeaderArray.map((el) => el.trim()).filter((string, index) => tempHeaderArray.indexOf(string) === index);
|
|
1673
|
-
fetchHeaders.set(key, tempHeaderArray.join(', '));
|
|
1674
|
-
});
|
|
1675
|
-
|
|
1676
|
-
curlHeaders = Array.from(fetchHeaders).map(([key, value]) => ` -H '${key}: ${value}'`).join('\\\n');
|
|
1677
|
-
if (curlHeaders) {
|
|
1678
|
-
curlHeaders = `${curlHeaders} \\\n`;
|
|
1679
|
-
}
|
|
1680
|
-
if (fetchOptions.body instanceof URLSearchParams) {
|
|
1681
|
-
curlData = ` -d ${fetchOptions.body.toString()} \\\n`;
|
|
1682
|
-
} else if (fetchOptions.body instanceof File) {
|
|
1683
|
-
curlData = ` --data-binary @${fetchOptions.body.name} \\\n`;
|
|
1684
|
-
} else if (fetchOptions.body instanceof FormData) {
|
|
1685
|
-
curlForm = Array.from(fetchOptions.body).reduce((aggregator, [key, value]) => {
|
|
1686
|
-
if (value instanceof File) {
|
|
1687
|
-
return [...aggregator, ` -F "${key}=@${value.name}"`];
|
|
1688
|
-
}
|
|
1689
|
-
|
|
1690
|
-
const multiple = value.match(/([^,],)/gm);
|
|
1691
|
-
|
|
1692
|
-
if (multiple) {
|
|
1693
|
-
const multipleResults = multiple.map((one) => `-F "${key}[]=${one}"`);
|
|
1694
|
-
|
|
1695
|
-
return [...aggregator, ...multipleResults];
|
|
1696
|
-
}
|
|
1697
|
-
|
|
1698
|
-
return [...aggregator, ` -F "${key}=${value}"`];
|
|
1699
|
-
}, []).join('\\\n');
|
|
1700
|
-
} else if (requestBodyContainerEl && requestBodyContainerEl.dataset.selectedRequestBodyType) {
|
|
1701
|
-
const requestBodyType = requestBodyContainerEl.dataset.selectedRequestBodyType;
|
|
1702
|
-
const exampleTextAreaEl = requestPanelEl.querySelector('.request-body-param-user-input');
|
|
1703
|
-
if (exampleTextAreaEl?.value) {
|
|
1704
|
-
fetchOptions.body = exampleTextAreaEl.value;
|
|
1705
|
-
if (requestBodyType.includes('json')) {
|
|
1706
|
-
try {
|
|
1707
|
-
curlData = ` -d '${JSON.stringify(JSON.parse(exampleTextAreaEl.value))}' \\\n`;
|
|
1708
|
-
} catch {
|
|
1709
|
-
// Ignore.
|
|
1710
|
-
}
|
|
1711
|
-
}
|
|
1712
|
-
if (!curlData) {
|
|
1713
|
-
curlData = ` -d '${exampleTextAreaEl.value.replace(/'/g, '\'"\'"\'')}' \\\n`;
|
|
1714
|
-
}
|
|
1715
|
-
}
|
|
1716
|
-
}
|
|
1717
|
-
|
|
1718
|
-
return `${curl}${curlHeaders}${curlData}${curlForm}`;
|
|
1719
|
-
}
|
|
1720
|
-
|
|
1721
|
-
onAddRemoveFileInput(e, pname, ptype) {
|
|
1722
|
-
if (e.target.tagName.toLowerCase() !== 'button') {
|
|
1723
|
-
return;
|
|
1724
|
-
}
|
|
1725
|
-
|
|
1726
|
-
if (e.target.classList.contains('file-input-remove-btn')) {
|
|
1727
|
-
// Remove File Input Set
|
|
1728
|
-
const el = e.target.closest('.input-set');
|
|
1729
|
-
el.remove();
|
|
1730
|
-
return;
|
|
1731
|
-
}
|
|
1732
|
-
const el = e.target.closest('.file-input-container');
|
|
1733
|
-
|
|
1734
|
-
// Add File Input Set
|
|
1735
|
-
|
|
1736
|
-
// Container
|
|
1737
|
-
const newInputContainerEl = document.createElement('div');
|
|
1738
|
-
newInputContainerEl.setAttribute('class', 'input-set row');
|
|
1739
|
-
|
|
1740
|
-
// File Input
|
|
1741
|
-
const newInputEl = document.createElement('input');
|
|
1742
|
-
newInputEl.type = 'file';
|
|
1743
|
-
newInputEl.style = 'width:200px; margin-top:2px;';
|
|
1744
|
-
newInputEl.setAttribute('data-pname', pname);
|
|
1745
|
-
newInputEl.setAttribute('data-ptype', ptype.includes('form-urlencode') ? 'form-urlencode' : 'form-data');
|
|
1746
|
-
newInputEl.setAttribute('data-array', 'false');
|
|
1747
|
-
newInputEl.setAttribute('data-file-array', 'true');
|
|
1748
|
-
|
|
1749
|
-
// Remover Button
|
|
1750
|
-
const newRemoveBtnEl = document.createElement('button');
|
|
1751
|
-
newRemoveBtnEl.setAttribute('class', 'file-input-remove-btn');
|
|
1752
|
-
newRemoveBtnEl.innerHTML = '✕';
|
|
1753
|
-
|
|
1754
|
-
newInputContainerEl.appendChild(newInputEl);
|
|
1755
|
-
newInputContainerEl.appendChild(newRemoveBtnEl);
|
|
1756
|
-
el.insertBefore(newInputContainerEl, e.target);
|
|
1757
|
-
// el.appendChild(newInputContainerEl);
|
|
1758
|
-
}
|
|
1759
|
-
|
|
1760
|
-
clearResponseData() {
|
|
1761
|
-
this.responseUrl = '';
|
|
1762
|
-
this.responseHeaders = '';
|
|
1763
|
-
this.responseText = '';
|
|
1764
|
-
this.responseStatus = 'success';
|
|
1765
|
-
this.responseMessage = '';
|
|
1766
|
-
this.responseIsBlob = false;
|
|
1767
|
-
this.responseBlobType = '';
|
|
1768
|
-
this.respContentDisposition = '';
|
|
1769
|
-
if (this.responseBlobUrl) {
|
|
1770
|
-
URL.revokeObjectURL(this.responseBlobUrl);
|
|
1771
|
-
this.responseBlobUrl = '';
|
|
1772
|
-
}
|
|
1773
|
-
}
|
|
1774
|
-
|
|
1775
|
-
disconnectedCallback() {
|
|
1776
|
-
this.curlSyntax = '';
|
|
1777
|
-
// Cleanup ObjectURL for the blob data if this component created one
|
|
1778
|
-
if (this.responseBlobUrl) {
|
|
1779
|
-
URL.revokeObjectURL(this.responseBlobUrl);
|
|
1780
|
-
this.responseBlobUrl = '';
|
|
1781
|
-
}
|
|
1782
|
-
super.disconnectedCallback();
|
|
1783
|
-
}
|
|
1784
|
-
}
|
|
1785
|
-
|
|
1786
|
-
// Register the element with the browser
|
|
1787
|
-
customElements.define('api-request', ApiRequest);
|