openapi-explorer 2.2.725 → 2.2.730

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.
@@ -162,13 +162,17 @@ export default class ApiRequest extends LitElement {
162
162
  return keyed(id, html` <div id="api-request-${id}" class="api-request col regular-font request-panel ${this.renderStyle === 'focused' || this.callback === 'true' ? 'focused-mode' : 'view-mode'}"> <div class="${this.callback === 'true' ? 'tiny-title' : 'req-res-title'}"> ${this.callback === 'true' ? 'CALLBACK REQUEST' : getI18nText('operations.request')} </div> <div> ${this.inputParametersTemplate('path')} ${this.inputParametersTemplate('query')} ${this.inputParametersTemplate('header')} ${this.inputParametersTemplate('cookie')} ${this.requestBodyTemplate()} ${this.allowTry === 'false' ? '' : html`${this.apiCallTemplate()}`} </div> </div> `);
163
163
  }
164
164
  updated(changedProperties) {
165
+ // When the operation is changed, reset the display view properties
166
+ if (changedProperties.get('elementId')) {
167
+ this.activeResponseTab = 'curl';
168
+ }
165
169
  // In focused mode after rendering the request component, update the text-areas(which contains examples) using the original values from hidden textareas.
166
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
167
171
  if (this.renderStyle !== 'focused') {
168
172
  return;
169
173
  }
170
174
 
171
- // dont update example as only tabs is switched
175
+ // don't update example as only tabs is switched
172
176
  if (changedProperties.size === 1 && changedProperties.has('activeSchemaTab')) {
173
177
  return;
174
178
  }
@@ -195,7 +199,7 @@ export default class ApiRequest extends LitElement {
195
199
  }[paramLocation];
196
200
  const tableRows = [];
197
201
  for (const param of filteredParams) {
198
- var _this$storedParamValu, _paramSchema$allowedV;
202
+ var _param$style, _param$explode;
199
203
  if (!param.schema) {
200
204
  continue;
201
205
  }
@@ -207,33 +211,57 @@ export default class ApiRequest extends LitElement {
207
211
  continue;
208
212
  }
209
213
  const defaultVal = Array.isArray(paramSchema.default) ? paramSchema.default : `${paramSchema.default}`;
210
- let paramStyle = 'form';
211
- let paramExplode = true;
212
- if (paramLocation === 'query') {
213
- if (param.style && 'form spaceDelimited pipeDelimited'.includes(param.style)) {
214
- paramStyle = param.style;
215
- }
216
- if (typeof param.explode === 'boolean') {
217
- paramExplode = param.explode;
218
- }
214
+ // Set the default style: https://spec.openapis.org/oas/v3.1.0.html#fixed-fields-9
215
+ const paramStyle = (_param$style = param.style) !== null && _param$style !== void 0 ? _param$style : {
216
+ query: 'form',
217
+ path: 'simple',
218
+ header: 'simple',
219
+ cookie: 'form'
220
+ }[paramLocation];
221
+ const paramExplode = (_param$explode = param.explode) !== null && _param$explode !== void 0 ? _param$explode : param.style === 'form';
222
+ const rowGenerator = ({
223
+ name: paramName,
224
+ description: paramDescription,
225
+ required: paramRequired
226
+ }, generatedParamSchema) => {
227
+ var _this$storedParamValu, _generatedParamSchema;
228
+ const displayAllowedValuesHints = (generatedParamSchema.type === 'object' || generatedParamSchema.type === 'array') && generatedParamSchema.allowedValues;
229
+ return html` <tr> <td colspan="1" style="width:160px;min-width:50px;vertical-align:top"> <div class="param-name ${generatedParamSchema.deprecated ? 'deprecated' : ''}" style="margin-top:1rem"> ${paramName}${!generatedParamSchema.deprecated && paramRequired ? html`<span style="color:var(--red)">*</span>` : ''} </div> <div class="param-type" style="margin-bottom:1rem"> ${generatedParamSchema.type === 'array' ? `${generatedParamSchema.arrayType}` : `${generatedParamSchema.format ? generatedParamSchema.format : generatedParamSchema.type}`}${!generatedParamSchema.deprecated && paramRequired ? html`<span style="opacity:0">*</span>` : ''} </div> </td> <td colspan="2" style="min-width:160px;vertical-align:top"> ${this.allowTry === 'true' ? generatedParamSchema.type === 'array' && html` <div style="margin-top:1rem;margin-bottom:1rem"> <tag-input class="request-param" autocomplete="on" id="request-param-${paramName}" style="width:100%" data-ptype="${paramLocation}" data-pname="${paramName}" 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 => {
230
+ this.storedParamValues[paramName] = e.detail.value;
231
+ this.computeCurlSyntax();
232
+ }}" .value="${(_this$storedParamValu = this.storedParamValues[paramName]) !== null && _this$storedParamValu !== void 0 ? _this$storedParamValu : this.fillRequestWithDefault === 'true' && Array.isArray(defaultVal) ? defaultVal : defaultVal.split(',')}"></tag-input> </div>` || generatedParamSchema.type === 'object' && html` <textarea autocomplete="on" id="request-param-${paramName}" @input="${() => {
233
+ this.computeCurlSyntax();
234
+ }}" class="textarea small request-param" part="textarea small textarea-param" rows="3" data-ptype="${paramLocation}" data-pname="${paramName}" data-default="${defaultVal}" data-param-serialize-style="${paramStyle}" data-param-serialize-explode="${paramExplode}" spellcheck="false" placeholder="${generatedParamSchema.example || defaultVal || ''}" style="width:100%;margin-top:1rem;margin-bottom:1rem" .value="${this.fillRequestWithDefault === 'true' ? defaultVal : ''}"></textarea>` || generatedParamSchema.allowedValues && html` <select aria-label="mime type" style="width:100%;margin-top:1rem;margin-bottom:1rem" data-ptype="${paramLocation}" data-pname="${paramName}" .value="${this.fillRequestWithDefault === 'true' ? defaultVal : ''}" @change="${e => {
235
+ this.storedParamValues[paramName] = e;
236
+ this.computeCurlSyntax();
237
+ }}"> ${generatedParamSchema.allowedValues.map(allowedValue => html` <option value="${allowedValue}" ?selected="${allowedValue === this.storedParamValues[paramName]}"> ${allowedValue === null ? '-' : allowedValue} </option>`)} </select>` || html` <input type="${generatedParamSchema.format === 'password' ? 'password' : 'text'}" spellcheck="false" style="width:100%;margin-top:1rem;margin-bottom:1rem" autocomplete="on" id="request-param-${paramName}" @input="${() => {
238
+ this.computeCurlSyntax();
239
+ }}" placeholder="${generatedParamSchema.example || defaultVal || ''}" class="request-param" part="textbox textbox-param" data-ptype="${paramLocation}" data-pname="${paramName}" data-default="${Array.isArray(defaultVal) ? defaultVal.join('~|~') : defaultVal}" data-array="false" @keyup="${this.requestParamFunction}" .value="${this.fillRequestWithDefault === 'true' ? defaultVal : ''}">` : ''} ${this.exampleListTemplate.call(this, param, generatedParamSchema.type)} </td> ${this.renderStyle === 'focused' ? html` <td colspan="2" style="vertical-align:top"> ${paramDescription ? html` <div class="param-description" style="margin-top:1rem"> ${unsafeHTML(toMarkdown(paramDescription))} </div>` : ''} ${generatedParamSchema.constraints.length || displayAllowedValuesHints || generatedParamSchema.pattern ? html` <div class="param-constraint" style="margin-top:1rem"> ${generatedParamSchema.constraints.length ? html`<span style="font-weight:700">Constraints: </span>${generatedParamSchema.constraints.join(', ')}<br>` : ''} ${generatedParamSchema.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">${generatedParamSchema.pattern}</div> <br> <div class="tooltip-text" style="position:absolute;display:block">${generatedParamSchema.pattern}</div> </div> ` : ''} ${(_generatedParamSchema = generatedParamSchema.allowedValues) === null || _generatedParamSchema === void 0 ? void 0 : _generatedParamSchema.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="${generatedParamSchema.type === 'array' ? 'array' : 'string'}" data-enum="${v === null || v === void 0 ? void 0 : v.trim()}" @click="${e => {
240
+ const inputEl = e.target.closest('table').querySelector(`[data-pname="${paramName}"]`);
241
+ if (inputEl) {
242
+ inputEl.value = e.target.dataset.type === 'array' ? [e.target.dataset.enum] : e.target.dataset.enum;
243
+ }
244
+ }}"> ${v === null ? '-' : v} </a>`}`)} </div>` : ''} </td> ` : ''} </tr>`;
245
+ };
246
+ let newRows = [];
247
+ if (paramStyle === 'form' && paramExplode) {
248
+ newRows = Object.keys(param.schema.properties).map(explodedParamKey => {
249
+ var _param$schema, _param$schema$require;
250
+ const explodedParam = param.schema.properties[explodedParamKey];
251
+ const explodedParamSchema = getTypeInfo(explodedParam, {
252
+ includeNulls: this.includeNulls,
253
+ enableExampleGeneration: true
254
+ });
255
+ return rowGenerator({
256
+ name: explodedParamKey,
257
+ description: explodedParam.description,
258
+ required: (_param$schema = param.schema) === null || _param$schema === void 0 ? void 0 : (_param$schema$require = _param$schema.required) === null || _param$schema$require === void 0 ? void 0 : _param$schema$require.includes(explodedParamKey)
259
+ }, explodedParamSchema);
260
+ });
261
+ } else {
262
+ newRows = rowGenerator(param, paramSchema);
219
263
  }
220
- const displayAllowedValuesHints = (paramSchema.type === 'object' || paramSchema.type === 'array') && paramSchema.allowedValues;
221
- 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" autocomplete="on" id="request-param-${param.name}" 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 => {
222
- this.storedParamValues[param.name] = e.detail.value;
223
- this.computeCurlSyntax();
224
- }}" .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 autocomplete="on" id="request-param-${param.name}" @input="${() => {
225
- this.computeCurlSyntax();
226
- }}" 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>` || paramSchema.allowedValues && html` <select aria-label="mime type" style="width:100%;margin-top:1rem;margin-bottom:1rem" data-ptype="${paramLocation}" data-pname="${param.name}" .value="${this.fillRequestWithDefault === 'true' ? defaultVal : ''}" @change="${e => {
227
- this.storedParamValues[param.name] = e;
228
- this.computeCurlSyntax();
229
- }}"> ${paramSchema.allowedValues.map(allowedValue => html` <option value="${allowedValue}" ?selected="${allowedValue === this.storedParamValues[param.name]}"> ${allowedValue === null ? '-' : allowedValue} </option>`)} </select>` || html` <input type="${paramSchema.format === 'password' ? 'password' : 'text'}" spellcheck="false" style="width:100%;margin-top:1rem;margin-bottom:1rem" autocomplete="on" id="request-param-${param.name}" @input="${() => {
230
- this.computeCurlSyntax();
231
- }}" 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(toMarkdown(param.description))} </div>` : ''} ${paramSchema.constraints.length || displayAllowedValuesHints || 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$allowedV = paramSchema.allowedValues) === null || _paramSchema$allowedV === void 0 ? void 0 : _paramSchema$allowedV.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 === null || v === void 0 ? void 0 : v.trim()}" @click="${e => {
232
- const inputEl = e.target.closest('table').querySelector(`[data-pname="${param.name}"]`);
233
- if (inputEl) {
234
- inputEl.value = e.target.dataset.type === 'array' ? [e.target.dataset.enum] : e.target.dataset.enum;
235
- }
236
- }}"> ${v === null ? '-' : v} </a>`}`)} </div>` : ''} </td> ` : ''} </tr>`);
264
+ tableRows.push(newRows);
237
265
  }
238
266
  return html` <div class="table-title top-gap">${title}${paramLocation === 'path' ? html`<span style="color:var(--red)">*</span>` : ''}</div> <div style="display:block;overflow-x:auto;max-width:100%"> <table role="presentation" class="m-table" style="width:100%;word-break:break-word"> ${tableRows} </table> </div>`;
239
267
  }
@@ -427,7 +455,7 @@ export default class ApiRequest extends LitElement {
427
455
  return;
428
456
  }
429
457
  this.activeResponseTab = e.target.dataset.tab;
430
- }}"> <br> <div style="width:100%"> <button class="tab-btn ${!hasResponse || this.activeResponseTab === 'curl' ? 'active' : ''}" data-tab="curl">FULL REQUEST</button> ${!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>`} </div> </div> ${this.responseIsBlob ? html` <div class="tab-content col" style="flex:1;display:${this.activeResponseTab === 'response' ? 'flex' : 'none'}"> ${this.responseBlobType === 'image' ? html`<img style="max-height:var(--resp-area-height,300px);object-fit:contain" class="mar-top-8" src="${this.responseBlobUrl}">` : ''} <div style="display:flex;justify-content:center"> <div> <button class="m-btn thin-border mar-top-8" style="width:135px" @click="${this.downloadResponseBlob}" part="btn btn-outline">DOWNLOAD</button> ${this.responseBlobType === 'view' || this.responseBlobType === 'image' ? 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> </div> </div>` : html` <div class="tab-content col m-markdown" style="flex:1;display:${this.activeResponseTab === 'response' ? 'flex' : 'none'}"> <syntax-highlighter style="min-height:60px" mime-type="${this.responseContentType}" .content="${this.responseText}"> </div>`} <div class="tab-content col m-markdown" style="flex:1;display:${this.activeResponseTab === 'headers' ? 'flex' : 'none'}"> <syntax-highlighter language="http" .content="${this.responseHeaders}"> </div> <div class="tab-content m-markdown col" style="flex:1;display:${this.activeResponseTab === 'curl' ? 'flex' : 'none'}"> <syntax-highlighter language="shell" .content="${curlSyntax.trim()}"> </div> </div>`;
458
+ }}"> <br> <div style="width:100%"> <button class="tab-btn ${!hasResponse || this.activeResponseTab === 'curl' ? 'active' : ''}" data-tab="curl">REQUEST</button> ${!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>`} </div> </div> ${this.responseIsBlob ? html` <div class="tab-content col" style="flex:1;display:${this.activeResponseTab === 'response' ? 'flex' : 'none'}"> ${this.responseBlobType === 'image' ? html`<img style="max-height:var(--resp-area-height,300px);object-fit:contain" class="mar-top-8" src="${this.responseBlobUrl}">` : ''} <div style="display:flex;justify-content:center"> <div> <button class="m-btn thin-border mar-top-8" style="width:135px" @click="${this.downloadResponseBlob}" part="btn btn-outline">DOWNLOAD</button> ${this.responseBlobType === 'view' || this.responseBlobType === 'image' ? 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> </div> </div>` : html` <div class="tab-content col m-markdown" style="flex:1;display:${this.activeResponseTab === 'response' ? 'flex' : 'none'}"> <syntax-highlighter style="min-height:60px" mime-type="${this.responseContentType}" .content="${this.responseText}"> </div>`} <div class="tab-content col m-markdown" style="flex:1;display:${this.activeResponseTab === 'headers' ? 'flex' : 'none'}"> <syntax-highlighter style="min-height:60px" language="http" .content="${this.responseHeaders}"> </div> <div class="tab-content m-markdown col" style="flex:1;display:${this.activeResponseTab === 'curl' ? 'flex' : 'none'}"> <syntax-highlighter style="min-height:60px" language="shell" .content="${curlSyntax.trim()}"> </div> </div>`;
431
459
  }
432
460
  apiCallTemplate() {
433
461
  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()} `;
@@ -455,11 +483,20 @@ export default class ApiRequest extends LitElement {
455
483
  this.dispatchEvent(new CustomEvent('event', event));
456
484
  this.computeCurlSyntax();
457
485
  }
486
+ validateAllRequestParameters() {
487
+ const requestPanelEl = this.closest('.request-panel');
488
+ const pathParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='path']")];
489
+ const missingPathParameterValue = pathParamEls.find(el => !el.value);
490
+ if (missingPathParameterValue) {
491
+ const error = Error(`All path parameters are required and a valid value was not found for the parameter: '${missingPathParameterValue.dataset.pname}'.`);
492
+ error.code = 'MissingPathParameter';
493
+ throw error;
494
+ }
495
+ }
458
496
  recomputeFetchOptions() {
459
497
  const requestPanelEl = this.closest('.request-panel');
460
498
  const pathParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='path']")];
461
499
  const queryParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='query']")];
462
- const queryParamObjTypeEls = [...requestPanelEl.querySelectorAll("[data-ptype='query-object']")];
463
500
  const headerParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='header']")];
464
501
  const requestBodyContainerEl = requestPanelEl.querySelector('.request-body-container');
465
502
  let pathUrl = `${this.serverUrl.replace(/\/$/, '')}${this.path.replaceAll(' ', '')}`;
@@ -470,12 +507,6 @@ export default class ApiRequest extends LitElement {
470
507
  pathParameterMap[el.dataset.pname] = el.value;
471
508
  pathUrl = pathUrl.replace(`{${el.dataset.pname}}`, encodeURIComponent(el.value) || '-');
472
509
  });
473
- const missingPathParameterValue = pathParamEls.find(el => !el.value);
474
- if (missingPathParameterValue) {
475
- const error = Error(`All path parameters are required and a valid value was not found for the parameter: '${missingPathParameterValue.dataset.pname}'.`);
476
- error.code = 'MissingPathParameter';
477
- throw error;
478
- }
479
510
 
480
511
  // Handle relative serverUrls
481
512
  if (!pathUrl.startsWith('http')) {
@@ -507,7 +538,7 @@ export default class ApiRequest extends LitElement {
507
538
  } else if (paramSerializeStyle === 'pipeDelimited') {
508
539
  fetchUrl.searchParams.append(el.dataset.pname, values.join('|').replace(/^\||\|$/g, ''));
509
540
  } else {
510
- if (paramSerializeExplode === 'true') {
541
+ if (paramSerializeExplode === 'true' || paramSerializeExplode === true) {
511
542
  // eslint-disable-line no-lonely-if
512
543
  values.forEach(v => {
513
544
  fetchUrl.searchParams.append(el.dataset.pname, v);
@@ -520,40 +551,6 @@ export default class ApiRequest extends LitElement {
520
551
  }
521
552
  });
522
553
 
523
- // Query Params (Dynamic - create from JSON)
524
- queryParamObjTypeEls.map(el => {
525
- try {
526
- let queryParamObj = {};
527
- const paramSerializeStyle = el.dataset.paramSerializeStyle;
528
- const paramSerializeExplode = el.dataset.paramSerializeExplode;
529
- queryParamObj = Object.assign(queryParamObj, JSON.parse(el.value.replace(/\s+/g, ' ')));
530
- for (const key in queryParamObj) {
531
- if (typeof queryParamObj[key] === 'object') {
532
- if (Array.isArray(queryParamObj[key])) {
533
- if (paramSerializeStyle === 'spaceDelimited') {
534
- fetchUrl.searchParams.append(key, queryParamObj[key].join(' '));
535
- } else if (paramSerializeStyle === 'pipeDelimited') {
536
- fetchUrl.searchParams.append(key, queryParamObj[key].join('|'));
537
- } else {
538
- if (paramSerializeExplode === 'true') {
539
- // eslint-disable-line no-lonely-if
540
- queryParamObj[key].forEach(v => {
541
- fetchUrl.searchParams.append(key, v);
542
- });
543
- } else {
544
- fetchUrl.searchParams.append(key, queryParamObj[key]);
545
- }
546
- }
547
- }
548
- } else {
549
- fetchUrl.searchParams.append(key, queryParamObj[key]);
550
- }
551
- }
552
- } catch (err) {
553
- console.log('OpenAPI Explorer: unable to parse %s into object', el.value); // eslint-disable-line no-console
554
- }
555
- });
556
-
557
554
  // Add Authentication api keys if provided
558
555
  this.api_keys.filter(v => v.finalKeyValue).forEach(v => {
559
556
  if (v.in === 'query') {
@@ -702,7 +699,7 @@ export default class ApiRequest extends LitElement {
702
699
  } = this.recomputeFetchOptions();
703
700
  const curl = `curl -X ${this.method.toUpperCase()} "${fetchUrl.toString()}"`;
704
701
  const headers = headerOverride !== null && headerOverride !== void 0 ? headerOverride : fetchOptions.headers;
705
- const curlHeaders = [...headers.entries()].reduce((acc, [key, value]) => `${acc} \\\n -H "${key}: ${value}"`, '');
702
+ const curlHeaders = [...headers.entries()].reduce((acc, [key, value]) => `${acc} \\\n -H "${key}: ${value.replace(/"/g, '\\"')}"`, '');
706
703
  this.curlSyntax = `${curl}${curlHeaders}${curlParts.data}${curlParts.form}`;
707
704
  } catch (error) {
708
705
  /* There was an explicit issue and likely it was because the fetch options threw. */
@@ -734,6 +731,16 @@ export default class ApiRequest extends LitElement {
734
731
  this.activeResponseTab = 'response';
735
732
  return;
736
733
  }
734
+ try {
735
+ this.validateAllRequestParameters();
736
+ } catch (error) {
737
+ this.responseMessage = error.message;
738
+ this.responseStatus = 'error';
739
+ this.responseUrl = '';
740
+ this.responseHeaders = '';
741
+ this.responseText = '';
742
+ return;
743
+ }
737
744
  this.responseIsBlob = false;
738
745
  this.respContentDisposition = '';
739
746
  if (this.responseBlobUrl) {
@@ -167,13 +167,17 @@ class ApiRequest extends _lit.LitElement {
167
167
  return (0, _keyed.keyed)(id, (0, _lit.html)` <div id="api-request-${id}" class="api-request col regular-font request-panel ${this.renderStyle === 'focused' || this.callback === 'true' ? 'focused-mode' : 'view-mode'}"> <div class="${this.callback === 'true' ? 'tiny-title' : 'req-res-title'}"> ${this.callback === 'true' ? 'CALLBACK REQUEST' : (0, _index.getI18nText)('operations.request')} </div> <div> ${this.inputParametersTemplate('path')} ${this.inputParametersTemplate('query')} ${this.inputParametersTemplate('header')} ${this.inputParametersTemplate('cookie')} ${this.requestBodyTemplate()} ${this.allowTry === 'false' ? '' : (0, _lit.html)`${this.apiCallTemplate()}`} </div> </div> `);
168
168
  }
169
169
  updated(changedProperties) {
170
+ // When the operation is changed, reset the display view properties
171
+ if (changedProperties.get('elementId')) {
172
+ this.activeResponseTab = 'curl';
173
+ }
170
174
  // In focused mode after rendering the request component, update the text-areas(which contains examples) using the original values from hidden textareas.
171
175
  // 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
172
176
  if (this.renderStyle !== 'focused') {
173
177
  return;
174
178
  }
175
179
 
176
- // dont update example as only tabs is switched
180
+ // don't update example as only tabs is switched
177
181
  if (changedProperties.size === 1 && changedProperties.has('activeSchemaTab')) {
178
182
  return;
179
183
  }
@@ -200,7 +204,7 @@ class ApiRequest extends _lit.LitElement {
200
204
  }[paramLocation];
201
205
  const tableRows = [];
202
206
  for (const param of filteredParams) {
203
- var _this$storedParamValu, _paramSchema$allowedV;
207
+ var _param$style, _param$explode;
204
208
  if (!param.schema) {
205
209
  continue;
206
210
  }
@@ -212,33 +216,57 @@ class ApiRequest extends _lit.LitElement {
212
216
  continue;
213
217
  }
214
218
  const defaultVal = Array.isArray(paramSchema.default) ? paramSchema.default : `${paramSchema.default}`;
215
- let paramStyle = 'form';
216
- let paramExplode = true;
217
- if (paramLocation === 'query') {
218
- if (param.style && 'form spaceDelimited pipeDelimited'.includes(param.style)) {
219
- paramStyle = param.style;
220
- }
221
- if (typeof param.explode === 'boolean') {
222
- paramExplode = param.explode;
223
- }
219
+ // Set the default style: https://spec.openapis.org/oas/v3.1.0.html#fixed-fields-9
220
+ const paramStyle = (_param$style = param.style) !== null && _param$style !== void 0 ? _param$style : {
221
+ query: 'form',
222
+ path: 'simple',
223
+ header: 'simple',
224
+ cookie: 'form'
225
+ }[paramLocation];
226
+ const paramExplode = (_param$explode = param.explode) !== null && _param$explode !== void 0 ? _param$explode : param.style === 'form';
227
+ const rowGenerator = ({
228
+ name: paramName,
229
+ description: paramDescription,
230
+ required: paramRequired
231
+ }, generatedParamSchema) => {
232
+ var _this$storedParamValu, _generatedParamSchema;
233
+ const displayAllowedValuesHints = (generatedParamSchema.type === 'object' || generatedParamSchema.type === 'array') && generatedParamSchema.allowedValues;
234
+ return (0, _lit.html)` <tr> <td colspan="1" style="width:160px;min-width:50px;vertical-align:top"> <div class="param-name ${generatedParamSchema.deprecated ? 'deprecated' : ''}" style="margin-top:1rem"> ${paramName}${!generatedParamSchema.deprecated && paramRequired ? (0, _lit.html)`<span style="color:var(--red)">*</span>` : ''} </div> <div class="param-type" style="margin-bottom:1rem"> ${generatedParamSchema.type === 'array' ? `${generatedParamSchema.arrayType}` : `${generatedParamSchema.format ? generatedParamSchema.format : generatedParamSchema.type}`}${!generatedParamSchema.deprecated && paramRequired ? (0, _lit.html)`<span style="opacity:0">*</span>` : ''} </div> </td> <td colspan="2" style="min-width:160px;vertical-align:top"> ${this.allowTry === 'true' ? generatedParamSchema.type === 'array' && (0, _lit.html)` <div style="margin-top:1rem;margin-bottom:1rem"> <tag-input class="request-param" autocomplete="on" id="request-param-${paramName}" style="width:100%" data-ptype="${paramLocation}" data-pname="${paramName}" 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 => {
235
+ this.storedParamValues[paramName] = e.detail.value;
236
+ this.computeCurlSyntax();
237
+ }}" .value="${(_this$storedParamValu = this.storedParamValues[paramName]) !== null && _this$storedParamValu !== void 0 ? _this$storedParamValu : this.fillRequestWithDefault === 'true' && Array.isArray(defaultVal) ? defaultVal : defaultVal.split(',')}"></tag-input> </div>` || generatedParamSchema.type === 'object' && (0, _lit.html)` <textarea autocomplete="on" id="request-param-${paramName}" @input="${() => {
238
+ this.computeCurlSyntax();
239
+ }}" class="textarea small request-param" part="textarea small textarea-param" rows="3" data-ptype="${paramLocation}" data-pname="${paramName}" data-default="${defaultVal}" data-param-serialize-style="${paramStyle}" data-param-serialize-explode="${paramExplode}" spellcheck="false" placeholder="${generatedParamSchema.example || defaultVal || ''}" style="width:100%;margin-top:1rem;margin-bottom:1rem" .value="${this.fillRequestWithDefault === 'true' ? defaultVal : ''}"></textarea>` || generatedParamSchema.allowedValues && (0, _lit.html)` <select aria-label="mime type" style="width:100%;margin-top:1rem;margin-bottom:1rem" data-ptype="${paramLocation}" data-pname="${paramName}" .value="${this.fillRequestWithDefault === 'true' ? defaultVal : ''}" @change="${e => {
240
+ this.storedParamValues[paramName] = e;
241
+ this.computeCurlSyntax();
242
+ }}"> ${generatedParamSchema.allowedValues.map(allowedValue => (0, _lit.html)` <option value="${allowedValue}" ?selected="${allowedValue === this.storedParamValues[paramName]}"> ${allowedValue === null ? '-' : allowedValue} </option>`)} </select>` || (0, _lit.html)` <input type="${generatedParamSchema.format === 'password' ? 'password' : 'text'}" spellcheck="false" style="width:100%;margin-top:1rem;margin-bottom:1rem" autocomplete="on" id="request-param-${paramName}" @input="${() => {
243
+ this.computeCurlSyntax();
244
+ }}" placeholder="${generatedParamSchema.example || defaultVal || ''}" class="request-param" part="textbox textbox-param" data-ptype="${paramLocation}" data-pname="${paramName}" data-default="${Array.isArray(defaultVal) ? defaultVal.join('~|~') : defaultVal}" data-array="false" @keyup="${this.requestParamFunction}" .value="${this.fillRequestWithDefault === 'true' ? defaultVal : ''}">` : ''} ${this.exampleListTemplate.call(this, param, generatedParamSchema.type)} </td> ${this.renderStyle === 'focused' ? (0, _lit.html)` <td colspan="2" style="vertical-align:top"> ${paramDescription ? (0, _lit.html)` <div class="param-description" style="margin-top:1rem"> ${(0, _unsafeHtml.unsafeHTML)((0, _commonUtils.toMarkdown)(paramDescription))} </div>` : ''} ${generatedParamSchema.constraints.length || displayAllowedValuesHints || generatedParamSchema.pattern ? (0, _lit.html)` <div class="param-constraint" style="margin-top:1rem"> ${generatedParamSchema.constraints.length ? (0, _lit.html)`<span style="font-weight:700">Constraints: </span>${generatedParamSchema.constraints.join(', ')}<br>` : ''} ${generatedParamSchema.pattern ? (0, _lit.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">${generatedParamSchema.pattern}</div> <br> <div class="tooltip-text" style="position:absolute;display:block">${generatedParamSchema.pattern}</div> </div> ` : ''} ${(_generatedParamSchema = generatedParamSchema.allowedValues) === null || _generatedParamSchema === void 0 ? void 0 : _generatedParamSchema.map((v, i) => (0, _lit.html)` ${i > 0 ? '|' : (0, _lit.html)`<span style="font-weight:700">Allowed: </span>`} ${(0, _lit.html)` <a part="anchor anchor-param-constraint" class="${this.allowTry === 'true' ? '' : 'inactive-link'}" data-type="${generatedParamSchema.type === 'array' ? 'array' : 'string'}" data-enum="${v === null || v === void 0 ? void 0 : v.trim()}" @click="${e => {
245
+ const inputEl = e.target.closest('table').querySelector(`[data-pname="${paramName}"]`);
246
+ if (inputEl) {
247
+ inputEl.value = e.target.dataset.type === 'array' ? [e.target.dataset.enum] : e.target.dataset.enum;
248
+ }
249
+ }}"> ${v === null ? '-' : v} </a>`}`)} </div>` : ''} </td> ` : ''} </tr>`;
250
+ };
251
+ let newRows = [];
252
+ if (paramStyle === 'form' && paramExplode) {
253
+ newRows = Object.keys(param.schema.properties).map(explodedParamKey => {
254
+ var _param$schema, _param$schema$require;
255
+ const explodedParam = param.schema.properties[explodedParamKey];
256
+ const explodedParamSchema = (0, _schemaUtils.getTypeInfo)(explodedParam, {
257
+ includeNulls: this.includeNulls,
258
+ enableExampleGeneration: true
259
+ });
260
+ return rowGenerator({
261
+ name: explodedParamKey,
262
+ description: explodedParam.description,
263
+ required: (_param$schema = param.schema) === null || _param$schema === void 0 ? void 0 : (_param$schema$require = _param$schema.required) === null || _param$schema$require === void 0 ? void 0 : _param$schema$require.includes(explodedParamKey)
264
+ }, explodedParamSchema);
265
+ });
266
+ } else {
267
+ newRows = rowGenerator(param, paramSchema);
224
268
  }
225
- const displayAllowedValuesHints = (paramSchema.type === 'object' || paramSchema.type === 'array') && paramSchema.allowedValues;
226
- tableRows.push((0, _lit.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 ? (0, _lit.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 ? (0, _lit.html)`<span style="opacity:0">*</span>` : ''} </div> </td> <td colspan="2" style="min-width:160px;vertical-align:top"> ${this.allowTry === 'true' ? paramSchema.type === 'array' && (0, _lit.html)` <div style="margin-top:1rem;margin-bottom:1rem"> <tag-input class="request-param" autocomplete="on" id="request-param-${param.name}" 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 => {
227
- this.storedParamValues[param.name] = e.detail.value;
228
- this.computeCurlSyntax();
229
- }}" .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' && (0, _lit.html)` <textarea autocomplete="on" id="request-param-${param.name}" @input="${() => {
230
- this.computeCurlSyntax();
231
- }}" 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>` || paramSchema.allowedValues && (0, _lit.html)` <select aria-label="mime type" style="width:100%;margin-top:1rem;margin-bottom:1rem" data-ptype="${paramLocation}" data-pname="${param.name}" .value="${this.fillRequestWithDefault === 'true' ? defaultVal : ''}" @change="${e => {
232
- this.storedParamValues[param.name] = e;
233
- this.computeCurlSyntax();
234
- }}"> ${paramSchema.allowedValues.map(allowedValue => (0, _lit.html)` <option value="${allowedValue}" ?selected="${allowedValue === this.storedParamValues[param.name]}"> ${allowedValue === null ? '-' : allowedValue} </option>`)} </select>` || (0, _lit.html)` <input type="${paramSchema.format === 'password' ? 'password' : 'text'}" spellcheck="false" style="width:100%;margin-top:1rem;margin-bottom:1rem" autocomplete="on" id="request-param-${param.name}" @input="${() => {
235
- this.computeCurlSyntax();
236
- }}" 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' ? (0, _lit.html)` <td colspan="2" style="vertical-align:top"> ${param.description ? (0, _lit.html)` <div class="param-description" style="margin-top:1rem"> ${(0, _unsafeHtml.unsafeHTML)((0, _commonUtils.toMarkdown)(param.description))} </div>` : ''} ${paramSchema.constraints.length || displayAllowedValuesHints || paramSchema.pattern ? (0, _lit.html)` <div class="param-constraint" style="margin-top:1rem"> ${paramSchema.constraints.length ? (0, _lit.html)`<span style="font-weight:700">Constraints: </span>${paramSchema.constraints.join(', ')}<br>` : ''} ${paramSchema.pattern ? (0, _lit.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$allowedV = paramSchema.allowedValues) === null || _paramSchema$allowedV === void 0 ? void 0 : _paramSchema$allowedV.map((v, i) => (0, _lit.html)` ${i > 0 ? '|' : (0, _lit.html)`<span style="font-weight:700">Allowed: </span>`} ${(0, _lit.html)` <a part="anchor anchor-param-constraint" class="${this.allowTry === 'true' ? '' : 'inactive-link'}" data-type="${paramSchema.type === 'array' ? 'array' : 'string'}" data-enum="${v === null || v === void 0 ? void 0 : v.trim()}" @click="${e => {
237
- const inputEl = e.target.closest('table').querySelector(`[data-pname="${param.name}"]`);
238
- if (inputEl) {
239
- inputEl.value = e.target.dataset.type === 'array' ? [e.target.dataset.enum] : e.target.dataset.enum;
240
- }
241
- }}"> ${v === null ? '-' : v} </a>`}`)} </div>` : ''} </td> ` : ''} </tr>`);
269
+ tableRows.push(newRows);
242
270
  }
243
271
  return (0, _lit.html)` <div class="table-title top-gap">${title}${paramLocation === 'path' ? (0, _lit.html)`<span style="color:var(--red)">*</span>` : ''}</div> <div style="display:block;overflow-x:auto;max-width:100%"> <table role="presentation" class="m-table" style="width:100%;word-break:break-word"> ${tableRows} </table> </div>`;
244
272
  }
@@ -432,7 +460,7 @@ class ApiRequest extends _lit.LitElement {
432
460
  return;
433
461
  }
434
462
  this.activeResponseTab = e.target.dataset.tab;
435
- }}"> <br> <div style="width:100%"> <button class="tab-btn ${!hasResponse || this.activeResponseTab === 'curl' ? 'active' : ''}" data-tab="curl">FULL REQUEST</button> ${!hasResponse ? '' : (0, _lit.html)` <button class="tab-btn ${this.activeResponseTab === 'response' ? 'active' : ''}" data-tab="response">${(0, _index.getI18nText)('operations.response')}</button> <button class="tab-btn ${this.activeResponseTab === 'headers' ? 'active' : ''}" data-tab="headers">${(0, _index.getI18nText)('operations.response-headers')}</button>`} </div> </div> ${this.responseIsBlob ? (0, _lit.html)` <div class="tab-content col" style="flex:1;display:${this.activeResponseTab === 'response' ? 'flex' : 'none'}"> ${this.responseBlobType === 'image' ? (0, _lit.html)`<img style="max-height:var(--resp-area-height,300px);object-fit:contain" class="mar-top-8" src="${this.responseBlobUrl}">` : ''} <div style="display:flex;justify-content:center"> <div> <button class="m-btn thin-border mar-top-8" style="width:135px" @click="${this.downloadResponseBlob}" part="btn btn-outline">DOWNLOAD</button> ${this.responseBlobType === 'view' || this.responseBlobType === 'image' ? (0, _lit.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> </div> </div>` : (0, _lit.html)` <div class="tab-content col m-markdown" style="flex:1;display:${this.activeResponseTab === 'response' ? 'flex' : 'none'}"> <syntax-highlighter style="min-height:60px" mime-type="${this.responseContentType}" .content="${this.responseText}"> </div>`} <div class="tab-content col m-markdown" style="flex:1;display:${this.activeResponseTab === 'headers' ? 'flex' : 'none'}"> <syntax-highlighter language="http" .content="${this.responseHeaders}"> </div> <div class="tab-content m-markdown col" style="flex:1;display:${this.activeResponseTab === 'curl' ? 'flex' : 'none'}"> <syntax-highlighter language="shell" .content="${curlSyntax.trim()}"> </div> </div>`;
463
+ }}"> <br> <div style="width:100%"> <button class="tab-btn ${!hasResponse || this.activeResponseTab === 'curl' ? 'active' : ''}" data-tab="curl">REQUEST</button> ${!hasResponse ? '' : (0, _lit.html)` <button class="tab-btn ${this.activeResponseTab === 'response' ? 'active' : ''}" data-tab="response">${(0, _index.getI18nText)('operations.response')}</button> <button class="tab-btn ${this.activeResponseTab === 'headers' ? 'active' : ''}" data-tab="headers">${(0, _index.getI18nText)('operations.response-headers')}</button>`} </div> </div> ${this.responseIsBlob ? (0, _lit.html)` <div class="tab-content col" style="flex:1;display:${this.activeResponseTab === 'response' ? 'flex' : 'none'}"> ${this.responseBlobType === 'image' ? (0, _lit.html)`<img style="max-height:var(--resp-area-height,300px);object-fit:contain" class="mar-top-8" src="${this.responseBlobUrl}">` : ''} <div style="display:flex;justify-content:center"> <div> <button class="m-btn thin-border mar-top-8" style="width:135px" @click="${this.downloadResponseBlob}" part="btn btn-outline">DOWNLOAD</button> ${this.responseBlobType === 'view' || this.responseBlobType === 'image' ? (0, _lit.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> </div> </div>` : (0, _lit.html)` <div class="tab-content col m-markdown" style="flex:1;display:${this.activeResponseTab === 'response' ? 'flex' : 'none'}"> <syntax-highlighter style="min-height:60px" mime-type="${this.responseContentType}" .content="${this.responseText}"> </div>`} <div class="tab-content col m-markdown" style="flex:1;display:${this.activeResponseTab === 'headers' ? 'flex' : 'none'}"> <syntax-highlighter style="min-height:60px" language="http" .content="${this.responseHeaders}"> </div> <div class="tab-content m-markdown col" style="flex:1;display:${this.activeResponseTab === 'curl' ? 'flex' : 'none'}"> <syntax-highlighter style="min-height:60px" language="shell" .content="${curlSyntax.trim()}"> </div> </div>`;
436
464
  }
437
465
  apiCallTemplate() {
438
466
  return (0, _lit.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 ? (0, _lit.html)` <button class="m-btn thin-border" part="btn btn-outline" style="margin-right:5px" @click="${this.onClearRequestData}"> ${(0, _index.getI18nText)('operations.clear')} </button>` : ''} <button class="m-btn primary btn-execute thin-border" part="btn btn-fill btn-try" @click="${this.onTryClick}">${(0, _index.getI18nText)('operations.execute')}</button> </div> ${this.apiResponseTabTemplate()} `;
@@ -460,11 +488,20 @@ class ApiRequest extends _lit.LitElement {
460
488
  this.dispatchEvent(new CustomEvent('event', event));
461
489
  this.computeCurlSyntax();
462
490
  }
491
+ validateAllRequestParameters() {
492
+ const requestPanelEl = this.closest('.request-panel');
493
+ const pathParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='path']")];
494
+ const missingPathParameterValue = pathParamEls.find(el => !el.value);
495
+ if (missingPathParameterValue) {
496
+ const error = Error(`All path parameters are required and a valid value was not found for the parameter: '${missingPathParameterValue.dataset.pname}'.`);
497
+ error.code = 'MissingPathParameter';
498
+ throw error;
499
+ }
500
+ }
463
501
  recomputeFetchOptions() {
464
502
  const requestPanelEl = this.closest('.request-panel');
465
503
  const pathParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='path']")];
466
504
  const queryParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='query']")];
467
- const queryParamObjTypeEls = [...requestPanelEl.querySelectorAll("[data-ptype='query-object']")];
468
505
  const headerParamEls = [...requestPanelEl.querySelectorAll("[data-ptype='header']")];
469
506
  const requestBodyContainerEl = requestPanelEl.querySelector('.request-body-container');
470
507
  let pathUrl = `${this.serverUrl.replace(/\/$/, '')}${this.path.replaceAll(' ', '')}`;
@@ -475,12 +512,6 @@ class ApiRequest extends _lit.LitElement {
475
512
  pathParameterMap[el.dataset.pname] = el.value;
476
513
  pathUrl = pathUrl.replace(`{${el.dataset.pname}}`, encodeURIComponent(el.value) || '-');
477
514
  });
478
- const missingPathParameterValue = pathParamEls.find(el => !el.value);
479
- if (missingPathParameterValue) {
480
- const error = Error(`All path parameters are required and a valid value was not found for the parameter: '${missingPathParameterValue.dataset.pname}'.`);
481
- error.code = 'MissingPathParameter';
482
- throw error;
483
- }
484
515
 
485
516
  // Handle relative serverUrls
486
517
  if (!pathUrl.startsWith('http')) {
@@ -512,7 +543,7 @@ class ApiRequest extends _lit.LitElement {
512
543
  } else if (paramSerializeStyle === 'pipeDelimited') {
513
544
  fetchUrl.searchParams.append(el.dataset.pname, values.join('|').replace(/^\||\|$/g, ''));
514
545
  } else {
515
- if (paramSerializeExplode === 'true') {
546
+ if (paramSerializeExplode === 'true' || paramSerializeExplode === true) {
516
547
  // eslint-disable-line no-lonely-if
517
548
  values.forEach(v => {
518
549
  fetchUrl.searchParams.append(el.dataset.pname, v);
@@ -525,40 +556,6 @@ class ApiRequest extends _lit.LitElement {
525
556
  }
526
557
  });
527
558
 
528
- // Query Params (Dynamic - create from JSON)
529
- queryParamObjTypeEls.map(el => {
530
- try {
531
- let queryParamObj = {};
532
- const paramSerializeStyle = el.dataset.paramSerializeStyle;
533
- const paramSerializeExplode = el.dataset.paramSerializeExplode;
534
- queryParamObj = Object.assign(queryParamObj, JSON.parse(el.value.replace(/\s+/g, ' ')));
535
- for (const key in queryParamObj) {
536
- if (typeof queryParamObj[key] === 'object') {
537
- if (Array.isArray(queryParamObj[key])) {
538
- if (paramSerializeStyle === 'spaceDelimited') {
539
- fetchUrl.searchParams.append(key, queryParamObj[key].join(' '));
540
- } else if (paramSerializeStyle === 'pipeDelimited') {
541
- fetchUrl.searchParams.append(key, queryParamObj[key].join('|'));
542
- } else {
543
- if (paramSerializeExplode === 'true') {
544
- // eslint-disable-line no-lonely-if
545
- queryParamObj[key].forEach(v => {
546
- fetchUrl.searchParams.append(key, v);
547
- });
548
- } else {
549
- fetchUrl.searchParams.append(key, queryParamObj[key]);
550
- }
551
- }
552
- }
553
- } else {
554
- fetchUrl.searchParams.append(key, queryParamObj[key]);
555
- }
556
- }
557
- } catch (err) {
558
- console.log('OpenAPI Explorer: unable to parse %s into object', el.value); // eslint-disable-line no-console
559
- }
560
- });
561
-
562
559
  // Add Authentication api keys if provided
563
560
  this.api_keys.filter(v => v.finalKeyValue).forEach(v => {
564
561
  if (v.in === 'query') {
@@ -707,7 +704,7 @@ class ApiRequest extends _lit.LitElement {
707
704
  } = this.recomputeFetchOptions();
708
705
  const curl = `curl -X ${this.method.toUpperCase()} "${fetchUrl.toString()}"`;
709
706
  const headers = headerOverride !== null && headerOverride !== void 0 ? headerOverride : fetchOptions.headers;
710
- const curlHeaders = [...headers.entries()].reduce((acc, [key, value]) => `${acc} \\\n -H "${key}: ${value}"`, '');
707
+ const curlHeaders = [...headers.entries()].reduce((acc, [key, value]) => `${acc} \\\n -H "${key}: ${value.replace(/"/g, '\\"')}"`, '');
711
708
  this.curlSyntax = `${curl}${curlHeaders}${curlParts.data}${curlParts.form}`;
712
709
  } catch (error) {
713
710
  /* There was an explicit issue and likely it was because the fetch options threw. */
@@ -739,6 +736,16 @@ class ApiRequest extends _lit.LitElement {
739
736
  this.activeResponseTab = 'response';
740
737
  return;
741
738
  }
739
+ try {
740
+ this.validateAllRequestParameters();
741
+ } catch (error) {
742
+ this.responseMessage = error.message;
743
+ this.responseStatus = 'error';
744
+ this.responseUrl = '';
745
+ this.responseHeaders = '';
746
+ this.responseText = '';
747
+ return;
748
+ }
742
749
  this.responseIsBlob = false;
743
750
  this.respContentDisposition = '';
744
751
  if (this.responseBlobUrl) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openapi-explorer",
3
- "version": "2.2.725",
3
+ "version": "2.2.730",
4
4
  "description": "OpenAPI Explorer - API viewer with dynamically generated components, documentation, and interaction console",
5
5
  "author": "Authress Developers <developers@authress.io>",
6
6
  "type": "module",