openapi-explorer 0.6.221 → 0.6.225
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/CHANGELOG.md +1 -0
- package/README.md +1 -1
- package/dist/openapi-explorer.min.js +4 -4
- package/dist/openapi-explorer.min.js.LICENSE.txt +1 -1
- package/dist/openapi-explorer.min.js.LICENSE.txt.gz +0 -0
- package/dist/openapi-explorer.min.js.gz +0 -0
- package/dist/openapi-explorer.min.js.map +1 -1
- package/dist/report.html +2 -2
- package/package.json +1 -1
- package/src/components/api-request.js +19 -18
- package/src/openapi-explorer.js +0 -17
- package/src/styles/api-request-styles.js +3 -0
- package/src/templates/endpoint-template.js +27 -15
- package/src/templates/navbar-template.js +9 -7
- package/src/templates/security-scheme-template.js +20 -11
- package/src/utils/schema-utils.js +2 -1
- package/src/utils/spec-parser.js +13 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "openapi-explorer",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.225",
|
|
4
4
|
"description": "OpenAPI Explorer - API viewer with dynamically generated components, documentation, and interaction console",
|
|
5
5
|
"author": "Rhosys Developers <developers@rhosys.ch>",
|
|
6
6
|
"repository": {
|
|
@@ -145,8 +145,8 @@ export default class ApiRequest extends LitElement {
|
|
|
145
145
|
tableRows.push(html`
|
|
146
146
|
<tr>
|
|
147
147
|
<td style="width:160px; min-width:50px;">
|
|
148
|
-
<div class="param-name">
|
|
149
|
-
${param.required ? html`<span style='color:var(--red)'>*</span>` : ''}
|
|
148
|
+
<div class="param-name ${paramSchema.deprecated ? 'deprecated' : ''}">
|
|
149
|
+
${param.name}${!paramSchema.deprecated && param.required ? html`<span style='color:var(--red);'>*</span>` : ''}
|
|
150
150
|
</div>
|
|
151
151
|
<div class="param-type">
|
|
152
152
|
${paramSchema.type === 'array'
|
|
@@ -497,11 +497,8 @@ export default class ApiRequest extends LitElement {
|
|
|
497
497
|
formDataTableRows.push(html`
|
|
498
498
|
<tr>
|
|
499
499
|
<td style="width:160px; min-width:100px;">
|
|
500
|
-
<div class="param-name">
|
|
501
|
-
${fieldSchema.required
|
|
502
|
-
? html`<span style='color:var(--red);'>*</span>${fieldName}`
|
|
503
|
-
: html`${fieldName}`
|
|
504
|
-
}
|
|
500
|
+
<div class="param-name ${fieldSchema.deprecated ? 'deprecated' : ''}">
|
|
501
|
+
${fieldName}${!fieldSchema.deprecated && (schema.required?.includes(fieldName) || fieldSchema.required) ? html`<span style='color:var(--red);'>*</span>` : ''}
|
|
505
502
|
</div>
|
|
506
503
|
<div class="param-type">${paramSchema.type}</div>
|
|
507
504
|
</td>
|
|
@@ -813,7 +810,7 @@ export default class ApiRequest extends LitElement {
|
|
|
813
810
|
fetchUrl = this.path;
|
|
814
811
|
const fetchOptions = {
|
|
815
812
|
method: this.method.toUpperCase(),
|
|
816
|
-
headers:
|
|
813
|
+
headers: new Headers()
|
|
817
814
|
};
|
|
818
815
|
// Generate URL using Path Params
|
|
819
816
|
pathParamEls.map((el) => {
|
|
@@ -896,7 +893,7 @@ export default class ApiRequest extends LitElement {
|
|
|
896
893
|
}
|
|
897
894
|
|
|
898
895
|
// Otherwise put it in the header
|
|
899
|
-
fetchOptions.headers
|
|
896
|
+
fetchOptions.headers.append(v.name, v.finalKeyValue);
|
|
900
897
|
curlHeaders += ` -H "${v.name}: ${v.finalKeyValue}" \\\n`;
|
|
901
898
|
});
|
|
902
899
|
|
|
@@ -912,17 +909,17 @@ export default class ApiRequest extends LitElement {
|
|
|
912
909
|
|
|
913
910
|
if (acceptHeader) {
|
|
914
911
|
// Uses the acceptHeader from Response panel
|
|
915
|
-
fetchOptions.headers.Accept
|
|
912
|
+
fetchOptions.headers.append('Accept', acceptHeader);
|
|
916
913
|
curlHeaders += ` -H "Accept: ${acceptHeader}" \\\n`;
|
|
917
914
|
} else if (this.accept) {
|
|
918
|
-
fetchOptions.headers.Accept
|
|
915
|
+
fetchOptions.headers.append('Accept', this.accept);
|
|
919
916
|
curlHeaders += ` -H "Accept: ${this.accept}" \\\n`;
|
|
920
917
|
}
|
|
921
918
|
|
|
922
919
|
// Add Header Params
|
|
923
920
|
headerParamEls.map((el) => {
|
|
924
921
|
if (el.value) {
|
|
925
|
-
fetchOptions.headers
|
|
922
|
+
fetchOptions.headers.append(el.dataset.pname, el.value);
|
|
926
923
|
curlHeaders += ` -H "${el.dataset.pname}: ${el.value}" \\\n`;
|
|
927
924
|
}
|
|
928
925
|
});
|
|
@@ -1004,18 +1001,22 @@ export default class ApiRequest extends LitElement {
|
|
|
1004
1001
|
const exampleTextAreaEl = requestPanelEl.querySelector('.request-body-param-user-input');
|
|
1005
1002
|
if (exampleTextAreaEl && exampleTextAreaEl.value) {
|
|
1006
1003
|
fetchOptions.body = exampleTextAreaEl.value;
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1004
|
+
if (requestBodyType.includes('json')) {
|
|
1005
|
+
try {
|
|
1006
|
+
curlData = ` -d '${JSON.stringify(JSON.parse(exampleTextAreaEl.value))}' \\\n`;
|
|
1007
|
+
} catch (err) { /* Ignore unparseable JSON */ }
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
if (!curlData) {
|
|
1011
|
+
// Save single quotes wrapped => 'text' => `"'"text"'"`
|
|
1012
|
+
curlData = ` -d '${exampleTextAreaEl.value.replace(/'/g, '\'"\'"\'')}' \\\n`;
|
|
1012
1013
|
}
|
|
1013
1014
|
}
|
|
1014
1015
|
}
|
|
1015
1016
|
// Common for all request-body
|
|
1016
1017
|
if (!requestBodyType.includes('form-data')) {
|
|
1017
1018
|
// For multipart/form-data don't set the content-type to allow creation of browser generated part boundaries
|
|
1018
|
-
fetchOptions.headers
|
|
1019
|
+
fetchOptions.headers.append('Content-Type', requestBodyType);
|
|
1019
1020
|
}
|
|
1020
1021
|
curlHeaders += ` -H "Content-Type: ${requestBodyType}" \\\n`;
|
|
1021
1022
|
}
|
package/src/openapi-explorer.js
CHANGED
|
@@ -199,23 +199,6 @@ export default class OpenApiExplorer extends LitElement {
|
|
|
199
199
|
background-image: linear-gradient(to right, rgba(0,0,0,0), var(--border-color), rgba(0,0,0,0));
|
|
200
200
|
}
|
|
201
201
|
|
|
202
|
-
.section-tag-header:hover::after {
|
|
203
|
-
position:absolute;
|
|
204
|
-
margin-left:-24px;
|
|
205
|
-
font-size:20px;
|
|
206
|
-
top: calc(50% - 14px);
|
|
207
|
-
color:var(--primary-color);
|
|
208
|
-
content: '⬆';
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
.collapsed .section-tag-header::after {
|
|
212
|
-
position:absolute;
|
|
213
|
-
margin-left:-24px;
|
|
214
|
-
font-size:20px;
|
|
215
|
-
top: calc(50% - 14px);
|
|
216
|
-
color: var(--border-color);
|
|
217
|
-
content: '⬇';
|
|
218
|
-
}
|
|
219
202
|
.collapsed .section-tag-header:hover::after {
|
|
220
203
|
color:var(--primary-color);
|
|
221
204
|
}
|
|
@@ -21,21 +21,33 @@ function toggleExpand(path) {
|
|
|
21
21
|
this.requestUpdate();
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
function toggleTag(tagElement, tagId) {
|
|
25
|
+
const sectionTag = tagElement.target.closest('.section-tag');
|
|
26
|
+
const tag = this.resolvedSpec.tags.find(t => t.elementId === tagId);
|
|
27
|
+
tag.expanded = !tag.expanded;
|
|
28
|
+
if (tag.expanded) {
|
|
29
|
+
sectionTag.classList.remove('collapsed');
|
|
30
|
+
sectionTag.classList.add('expanded');
|
|
30
31
|
} else {
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
});
|
|
32
|
+
sectionTag.classList.remove('expanded');
|
|
33
|
+
sectionTag.classList.add('collapsed');
|
|
34
34
|
}
|
|
35
|
+
this.requestUpdate();
|
|
35
36
|
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
export function expandCollapseAll(currentElement, action = 'expand-all') {
|
|
38
|
+
const operationsRootEl = currentElement.target.closest('.operations-root');
|
|
39
|
+
const elList = [...operationsRootEl.querySelectorAll('.section-tag')];
|
|
40
|
+
const expand = action === 'expand-all';
|
|
41
|
+
this.resolvedSpec.tags.forEach(t => t.expanded = expand);
|
|
42
|
+
elList.map((el) => {
|
|
43
|
+
if (expand) {
|
|
44
|
+
el.classList.remove('collapsed');
|
|
45
|
+
el.classList.add('expanded');
|
|
46
|
+
} else {
|
|
47
|
+
el.classList.remove('expanded');
|
|
48
|
+
el.classList.add('collapsed');
|
|
49
|
+
}
|
|
50
|
+
});
|
|
39
51
|
}
|
|
40
52
|
|
|
41
53
|
/* eslint-disable indent */
|
|
@@ -127,14 +139,14 @@ function endpointBodyTemplate(path) {
|
|
|
127
139
|
export default function endpointTemplate() {
|
|
128
140
|
return html`
|
|
129
141
|
<div style="display:flex; justify-content:flex-end; padding-right: 1rem; font-size: 14px;">
|
|
130
|
-
<span @click="${(e) =>
|
|
142
|
+
<span @click="${(e) => expandCollapseAll.call(this, e, 'expand-all')}" style="color:var(--primary-color); cursor: pointer;">Expand</span>
|
|
131
143
|
|
|
|
132
|
-
<span @click="${(e) =>
|
|
144
|
+
<span @click="${(e) => expandCollapseAll.call(this, e, 'collapse-all')}" style="color:var(--primary-color); cursor: pointer;">Collapse</span>
|
|
133
145
|
</div>
|
|
134
146
|
${(this.resolvedSpec && this.resolvedSpec.tags || []).map((tag) => html`
|
|
135
147
|
<div class='regular-font section-gap section-tag ${tag.expanded ? 'expanded' : 'collapsed'}' >
|
|
136
148
|
|
|
137
|
-
<div class='section-tag-header' @click="${() =>
|
|
149
|
+
<div class='section-tag-header' @click="${(e) => toggleTag.call(this, e, tag.elementId)}">
|
|
138
150
|
<div id='${tag.elementId}' class="sub-title tag" style="color:var(--primary-color)">${tag.name}</div>
|
|
139
151
|
</div>
|
|
140
152
|
<div class='section-tag-body'>
|
|
@@ -5,11 +5,13 @@ import { pathIsInSearch } from '../utils/common-utils';
|
|
|
5
5
|
export function expandCollapseNavBarTag(navLinkEl, action = 'toggle') {
|
|
6
6
|
const tagAndPathEl = navLinkEl && navLinkEl.closest('.nav-bar-tag-and-paths');
|
|
7
7
|
if (tagAndPathEl) {
|
|
8
|
-
const
|
|
9
|
-
if (
|
|
10
|
-
tagAndPathEl.classList.
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
const expand = tagAndPathEl.classList.contains('collapsed') && action === 'toggle' || action === 'expand';
|
|
9
|
+
if (expand) {
|
|
10
|
+
tagAndPathEl.classList.remove('collapsed');
|
|
11
|
+
tagAndPathEl.classList.add('expanded');
|
|
12
|
+
} else {
|
|
13
|
+
tagAndPathEl.classList.remove('expanded');
|
|
14
|
+
tagAndPathEl.classList.add('collapsed');
|
|
13
15
|
}
|
|
14
16
|
}
|
|
15
17
|
}
|
|
@@ -18,11 +20,11 @@ export function expandCollapseAll(navEl, action = 'expand-all') {
|
|
|
18
20
|
const elList = [...navEl.querySelectorAll('.nav-bar-tag-and-paths')];
|
|
19
21
|
if (action === 'expand-all') {
|
|
20
22
|
elList.map((el) => {
|
|
21
|
-
el
|
|
23
|
+
expandCollapseNavBarTag(el, 'expand');
|
|
22
24
|
});
|
|
23
25
|
} else {
|
|
24
26
|
elList.map((el) => {
|
|
25
|
-
el
|
|
27
|
+
expandCollapseNavBarTag(el, 'collapse');
|
|
26
28
|
});
|
|
27
29
|
}
|
|
28
30
|
}
|
|
@@ -386,25 +386,35 @@ export default function securitySchemeTemplate() {
|
|
|
386
386
|
`;
|
|
387
387
|
}
|
|
388
388
|
|
|
389
|
+
function getOauthScopeTemplate(scopes) {
|
|
390
|
+
if (!scopes || !scopes.length || !Array.isArray(scopes)) {
|
|
391
|
+
return '';
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
return html`
|
|
395
|
+
<div>
|
|
396
|
+
<b>Required scopes:</b>
|
|
397
|
+
<br/>
|
|
398
|
+
<div style="margin-left:8px">
|
|
399
|
+
${scopes.map(scope => html`<span>${scope}</span> `)}
|
|
400
|
+
</div>
|
|
401
|
+
</div>`;
|
|
402
|
+
}
|
|
403
|
+
|
|
389
404
|
export function pathSecurityTemplate(pathSecurity) {
|
|
390
405
|
if (this.resolvedSpec.securitySchemes && pathSecurity) {
|
|
391
406
|
const orSecurityKeys1 = [];
|
|
392
407
|
pathSecurity.forEach((pSecurity) => {
|
|
393
408
|
const andSecurityKeys1 = [];
|
|
394
409
|
const andKeyTypes = [];
|
|
395
|
-
let pathScopes = '';
|
|
396
410
|
Object.keys(pSecurity).forEach((pathSecurityKey) => {
|
|
397
411
|
const s = this.resolvedSpec.securitySchemes.find((ss) => ss.apiKeyId === pathSecurityKey);
|
|
398
|
-
if (!pathScopes) {
|
|
399
|
-
pathScopes = pSecurity[pathSecurityKey].join(', ');
|
|
400
|
-
}
|
|
401
412
|
if (s) {
|
|
402
413
|
andKeyTypes.push(s.typeDisplay);
|
|
403
|
-
andSecurityKeys1.push(s);
|
|
414
|
+
andSecurityKeys1.push({ ...s, scopes: pSecurity[pathSecurityKey] });
|
|
404
415
|
}
|
|
405
416
|
});
|
|
406
417
|
orSecurityKeys1.push({
|
|
407
|
-
pathScopes,
|
|
408
418
|
securityTypes: andKeyTypes.length > 1 ? `${andKeyTypes[0]} + ${andKeyTypes.length - 1} more` : andKeyTypes[0],
|
|
409
419
|
securityDefs: andSecurityKeys1,
|
|
410
420
|
});
|
|
@@ -430,22 +440,21 @@ export function pathSecurityTemplate(pathSecurity) {
|
|
|
430
440
|
? html`
|
|
431
441
|
<div>
|
|
432
442
|
${orSecurityItem1.securityDefs.length > 1 ? html`<b>${j + 1}.</b> ` : html`Requires`}
|
|
433
|
-
OAuth
|
|
434
|
-
${
|
|
435
|
-
? html`. Required scopes: <ul>${orSecurityItem1.pathScopes.split(',').map((scope) => html`<li>${scope}</li>`)}</ul>`
|
|
436
|
-
: ''
|
|
437
|
-
}
|
|
443
|
+
OAuth token (${andSecurityItem.apiKeyId}) in <b>Authorization header</b>
|
|
444
|
+
${getOauthScopeTemplate(andSecurityItem.scopes)}
|
|
438
445
|
</div>`
|
|
439
446
|
: andSecurityItem.type === 'http'
|
|
440
447
|
? html`
|
|
441
448
|
<div>
|
|
442
449
|
${orSecurityItem1.securityDefs.length > 1 ? html`<b>${j + 1}.</b> ` : html`Requires`}
|
|
443
450
|
${andSecurityItem.scheme === 'basic' ? 'Base 64 encoded username:password' : 'Bearer Token'} in <b>Authorization header</b>
|
|
451
|
+
${getOauthScopeTemplate(andSecurityItem.scopes)}
|
|
444
452
|
</div>`
|
|
445
453
|
: html`
|
|
446
454
|
<div>
|
|
447
455
|
${orSecurityItem1.securityDefs.length > 1 ? html`<b>${j + 1}.</b> ` : html`Requires`}
|
|
448
456
|
Token in <b>${andSecurityItem.name} ${andSecurityItem.in}</b>
|
|
457
|
+
${getOauthScopeTemplate(andSecurityItem.scopes)}
|
|
449
458
|
</div>`
|
|
450
459
|
}
|
|
451
460
|
`)}
|
|
@@ -631,7 +631,8 @@ export function schemaInObjectNotation(schema, obj, level = 0, suffix = '') {
|
|
|
631
631
|
}
|
|
632
632
|
|
|
633
633
|
/* Create Example object */
|
|
634
|
-
export function generateExample(examples, example, schema,
|
|
634
|
+
export function generateExample(examples, example, schema, rawMimeType, includeReadOnly = true, includeWriteOnly = true, outputType) {
|
|
635
|
+
const mimeType = rawMimeType || 'application/json';
|
|
635
636
|
const finalExamples = [];
|
|
636
637
|
// First check if examples is provided
|
|
637
638
|
if (examples) {
|
package/src/utils/spec-parser.js
CHANGED
|
@@ -54,7 +54,7 @@ export default async function ProcessSpec(requiresLookup, specUrlOrObject, gener
|
|
|
54
54
|
} else if (v.type === 'apiKey') {
|
|
55
55
|
v.typeDisplay = `API Key (${v.name})`;
|
|
56
56
|
} else if (v.type === 'oauth2') {
|
|
57
|
-
v.typeDisplay =
|
|
57
|
+
v.typeDisplay = 'OAuth2.0';
|
|
58
58
|
} else {
|
|
59
59
|
v.typeDisplay = v.type;
|
|
60
60
|
}
|
|
@@ -281,6 +281,18 @@ function groupByTags(openApiSpec, generateMissingTags = false) {
|
|
|
281
281
|
finalParameters = pathOrHookObj.parameters ? pathOrHookObj.parameters.slice(0) : [];
|
|
282
282
|
}
|
|
283
283
|
|
|
284
|
+
// Remove bad callbacks
|
|
285
|
+
if (pathOrHookObj.callbacks) {
|
|
286
|
+
for (const [callbackName, callbackConfig] of Object.entries(pathOrHookObj.callbacks)) {
|
|
287
|
+
const originalCallbackEntries = Object.entries(callbackConfig);
|
|
288
|
+
const filteredCallbacks = originalCallbackEntries.filter((entry) => typeof entry[1] === 'object') || [];
|
|
289
|
+
pathOrHookObj.callbacks[callbackName] = Object.fromEntries(filteredCallbacks);
|
|
290
|
+
if (filteredCallbacks.length !== originalCallbackEntries.length) {
|
|
291
|
+
console.warn(`OpenAPI Explorer: Invalid Callback found in ${callbackName}`); // eslint-disable-line no-console
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
284
296
|
// Update Responses
|
|
285
297
|
tagObj.paths.push({
|
|
286
298
|
show: true,
|