openapi-explorer 0.8.291 → 0.8.294

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openapi-explorer",
3
- "version": "0.8.291",
3
+ "version": "0.8.294",
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": {
@@ -245,7 +245,7 @@ export default class ApiRequest extends LitElement {
245
245
  return html`
246
246
  <div class="table-title top-gap">${title}${paramType === 'path' ? html`<span style='color:var(--red);'>*</span>` : ''}</div>
247
247
  <div style="display:block; overflow-x:auto; max-width:100%;">
248
- <table class="m-table" style="width:100%; word-break:break-word;">
248
+ <table role="presentation" class="m-table" style="width:100%; word-break:break-word;">
249
249
  ${tableRows}
250
250
  </table>
251
251
  </div>`;
@@ -313,7 +313,7 @@ export default class ApiRequest extends LitElement {
313
313
  reqBodyTypeSelectorHtml = requestBodyTypes.length === 1
314
314
  ? ''
315
315
  : html`
316
- <select style="min-width:100px; max-width:100%; margin-bottom:-1px;" @change = '${(e) => this.onMimeTypeChange(e)}'>
316
+ <select aria-label="mime type" style="min-width:100px; max-width:100%; margin-bottom:-1px;" @change = '${(e) => this.onMimeTypeChange(e)}'>
317
317
  ${requestBodyTypes.map((reqBody) => html`
318
318
  <option value = '${reqBody.mimeType}' ?selected = '${reqBody.mimeType === this.selectedRequestBodyType}'>
319
319
  ${reqBody.mimeType}
@@ -348,7 +348,7 @@ export default class ApiRequest extends LitElement {
348
348
  ${reqBodyExamples.length === 1
349
349
  ? ''
350
350
  : html`
351
- <select style="min-width:100px; max-width:100%; margin-bottom:-1px;" @change='${(e) => this.onSelectExample(e)}'>
351
+ <select aria-label='request body example' style="min-width:100px; max-width:100%; margin-bottom:-1px;" @change='${(e) => this.onSelectExample(e)}'>
352
352
  ${reqBodyExamples.map((v) => html`<option value="${v.exampleId}" ?selected=${v.exampleId === this.selectedRequestBodyExample} >
353
353
  ${v.exampleSummary.length > 80 ? v.exampleId : v.exampleSummary ? v.exampleSummary : v.exampleId}
354
354
  </option>`)}
@@ -683,7 +683,7 @@ export default class ApiRequest extends LitElement {
683
683
  }`);
684
684
  }
685
685
  return html`
686
- <table style="width:100%;" class="m-table">
686
+ <table role="presentation" style="width:100%;" class="m-table">
687
687
  ${formDataTableRows}
688
688
  </table>
689
689
  `;
@@ -216,7 +216,7 @@ export default class ApiResponse extends LitElement {
216
216
  responseHeaderListTemplate(respHeaders) {
217
217
  return html`
218
218
  <div style="padding:16px 0 8px 0" class="resp-headers small-font-size bold-text">RESPONSE HEADERS</div>
219
- <table style="border-collapse: collapse; margin-bottom:16px; border:1px solid var(--border-color); border-radius: var(--border-radius)" class="small-font-size mono-font">
219
+ <table role="presentation" style="border-collapse: collapse; margin-bottom:16px; border:1px solid var(--border-color); border-radius: var(--border-radius)" class="small-font-size mono-font">
220
220
  ${respHeaders.map((v) => html`
221
221
  <tr>
222
222
  <td style="padding:8px; vertical-align: baseline; min-width:120px; border-top: 1px solid var(--light-border-color); text-overflow: ellipsis;">
@@ -238,7 +238,7 @@ export default class ApiResponse extends LitElement {
238
238
 
239
239
  mimeTypeDropdownTemplate(mimeTypes) {
240
240
  return html`
241
- <select @change="${(e) => { this.selectedMimeType = e.target.value; }}" style='margin-bottom: -1px; z-index:1'>
241
+ <select aria-label='mime type' @change="${(e) => { this.selectedMimeType = e.target.value; }}" style='margin-bottom: -1px; z-index:1'>
242
242
  ${mimeTypes.map((mimeType) => html`<option value='${mimeType}' ?selected = '${mimeType === this.selectedMimeType}'> ${mimeType} </option>`)}
243
243
  </select>`;
244
244
  }
@@ -276,7 +276,7 @@ export default class ApiResponse extends LitElement {
276
276
  }`
277
277
  : html`
278
278
  <span class = 'example-panel generic-tree ${this.renderStyle === 'read' ? 'border pad-8-16' : 'border-top pad-top-8'}'>
279
- <select @change='${(e) => this.onSelectExample(e)}'>
279
+ <select aria-label='response body example' @change='${(e) => this.onSelectExample(e)}'>
280
280
  ${mimeRespDetails.examples.map((v) => html`<option value="${v.exampleId}" ?selected=${v.exampleId === mimeRespDetails.selectedExample} >
281
281
  ${v.exampleSummary.length > 80 ? v.exampleId : v.exampleSummary}
282
282
  </option>`)}
@@ -65,7 +65,6 @@ export default class OpenApiExplorer extends LitElement {
65
65
  defaultSchemaTab: { type: String, attribute: 'default-schema-tab' },
66
66
  responseAreaHeight: { type: String, attribute: 'response-area-height' },
67
67
  fillRequestWithDefault: { type: String, attribute: 'fill-defaults' },
68
- onNavTagClick: { type: String, attribute: 'on-nav-tag-click' },
69
68
 
70
69
  // Schema Styles
71
70
  schemaStyle: { type: String, attribute: 'schema-style' },
@@ -379,7 +378,6 @@ export default class OpenApiExplorer extends LitElement {
379
378
  this.schemaHideReadOnly = ['post', 'put', 'patch'].join(',');
380
379
  this.schemaHideWriteOnly = true;
381
380
  if (!this.fillRequestWithDefault || !'true, false,'.includes(`${this.fillRequestWithDefault},`)) { this.fillRequestWithDefault = 'true'; }
382
- if (!this.onNavTagClick || !'expand-collapse, show-description,'.includes(`${this.onNavTagClick},`)) { this.onNavTagClick = 'expand-collapse'; }
383
381
  if (!this.responseAreaHeight) {
384
382
  this.responseAreaHeight = '300px';
385
383
  }
@@ -510,16 +508,15 @@ export default class OpenApiExplorer extends LitElement {
510
508
  this.resolvedSpec = null;
511
509
  this.loading = true;
512
510
  this.loadFailed = false;
513
- const isServerUrl = typeof specUrlOrObject === 'string' && specUrlOrObject.match(/^http/);
514
- if (!this.serverUrl && isServerUrl) {
515
- this.serverUrl = new URL(specUrlOrObject).origin;
516
- }
517
- const spec = await ProcessSpec(isServerUrl, specUrlOrObject, this.serverUrl);
511
+ const spec = await ProcessSpec(specUrlOrObject, this.serverUrl);
518
512
  this.loading = false;
519
513
  if (spec === undefined || spec === null) {
520
514
  console.error('Unable to resolve the API spec. '); // eslint-disable-line no-console
521
515
  return;
522
516
  }
517
+ if (!this.serverUrl) {
518
+ this.serverUrl = spec.servers[0].url;
519
+ }
523
520
  this.afterSpecParsedAndValidated(spec);
524
521
  } catch (err) {
525
522
  this.loading = false;
@@ -29,14 +29,11 @@ export default css`
29
29
  justify-content: space-between;
30
30
  flex-direction: row;
31
31
  }
32
- .nav-bar.read .nav-bar-tag-icon {
33
- display:none;
34
- }
35
32
 
36
33
  .nav-bar-tag-icon {
34
+ font-size: 16px;
35
+ padding-right: 1px;
37
36
  color: var(--nav-text-color);
38
- font-size: 20px;
39
- margin-right: -16px;
40
37
  }
41
38
  .nav-bar-tag-icon:hover {
42
39
  color:var(--nav-hover-text-color);
@@ -44,15 +41,15 @@ export default css`
44
41
  .nav-bar.focused .nav-bar-tag-and-paths.collapsed .nav-bar-paths-under-tag {
45
42
  display:none;
46
43
  }
47
- .nav-bar.focused .nav-bar-tag-and-paths .nav-bar-tag-icon::after {
48
- content: '▸';
49
- font-size: 16px;
50
- text-align: center;
51
- display: inline-block;
44
+
45
+ .nav-bar-tag-and-paths.collapsed .nav-bar-tag-icon.collapse-button-arrow {
46
+ display: none;
52
47
  }
53
- .nav-bar.focused .nav-bar-tag-and-paths.expanded .nav-bar-tag-icon::after {
54
- transform: rotate(90deg);
48
+
49
+ .nav-bar-tag-and-paths.expanded .nav-bar-tag-icon.expand-button-arrow {
50
+ display: none;
55
51
  }
52
+
56
53
  .nav-bar.focused, .nav-scroll {
57
54
  border-top: 1px solid var(--secondary-color);
58
55
  }
@@ -71,7 +68,7 @@ export default css`
71
68
  color: var(--secondary-color);
72
69
  border-left:4px solid transparent;
73
70
  font-weight:bold;
74
- padding: 15px 30px 15px 10px;
71
+ padding: 15px 15px 15px 10px;
75
72
  text-transform: capitalize;
76
73
  }
77
74
 
@@ -108,9 +105,13 @@ export default css`
108
105
  font-size: var(--font-size-small);
109
106
  color: var(--nav-text-color);
110
107
  padding: 15px 15px 5px 5px;
111
- filter:opacity(0.5);
112
108
  font-weight:bold;
113
109
  border-bottom: 1px solid var(--nav-text-color);
110
+ background: var(--nav-bg-color);
111
+ }
112
+ .sticky-scroll-element {
113
+ position: sticky;
114
+ top: 0;
114
115
  }
115
116
 
116
117
  .nav-bar-h2 {margin-left:12px;}
@@ -1,6 +1,4 @@
1
1
  import { html } from 'lit-element';
2
- import { unsafeHTML } from 'lit-html/directives/unsafe-html';
3
- import { marked } from 'marked';
4
2
  import { expandedEndpointBodyTemplate } from './expanded-endpoint-template';
5
3
  import '../components/api-request';
6
4
  import '../components/api-response';
@@ -31,21 +29,7 @@ function defaultContentTemplate() {
31
29
 
32
30
  /* eslint-disable indent */
33
31
  function focusedTagBodyTemplate(tag) {
34
- return html`
35
- <h1 id="${tag.elementId}">${tag.name}</h1>
36
- ${this.onNavTagClick === 'show-description' && tag.description
37
- ? html`
38
- <div class="m-markdown">
39
- ${
40
- unsafeHTML(`
41
- <div class="m-markdown regular-font">
42
- ${marked(tag.description || '')}
43
- </div>`)
44
- }
45
- </div>`
46
- : ''
47
- }
48
- `;
32
+ return html`<h1 id="${tag.elementId}">${tag.name}</h1>`;
49
33
  }
50
34
 
51
35
  export default function focusedEndpointTemplate() {
@@ -8,32 +8,44 @@ export function expandCollapseNavBarTag(navLinkEl, action = 'toggle') {
8
8
  if (expand) {
9
9
  tagAndPathEl.classList.remove('collapsed');
10
10
  tagAndPathEl.classList.add('expanded');
11
- } else {
12
- tagAndPathEl.classList.remove('expanded');
13
- tagAndPathEl.classList.add('collapsed');
11
+ return;
14
12
  }
15
- }
16
- }
17
13
 
18
- export function expandCollapseAll(navEl, action = 'expand-all') {
19
- const elList = [...navEl.querySelectorAll('.nav-bar-tag-and-paths')];
20
- if (action === 'expand-all') {
21
- elList.map((el) => {
22
- expandCollapseNavBarTag(el, 'expand');
23
- });
24
- } else {
25
- elList.map((el) => {
26
- expandCollapseNavBarTag(el, 'collapse');
27
- });
14
+ tagAndPathEl.classList.remove('expanded');
15
+ tagAndPathEl.classList.add('collapsed');
28
16
  }
29
17
  }
30
18
 
31
- function onExpandCollapse(e) {
19
+ function onExpandCollapse(tagId, e) {
20
+ const tag = this.resolvedSpec.tags.find(t => t.elementId === tagId);
21
+ if (!tag) {
22
+ return;
23
+ }
32
24
  expandCollapseNavBarTag(e.target, 'toggle');
25
+ tag.expanded = !tag.expanded;
26
+
27
+ if (this.resolvedSpec.tags.some(t => t.expanded)) {
28
+ this.operationsCollapsed = false;
29
+ }
30
+
31
+ if (this.resolvedSpec.tags.every(t => !t.expanded)) {
32
+ this.operationsCollapsed = true;
33
+ }
33
34
  }
34
35
 
35
- function onExpandCollapseAll(e, action = 'expand-all') {
36
- expandCollapseAll(e.target.closest('.nav-scroll'), action);
36
+ export function expandCollapseAll(e, action = 'expand-all') {
37
+ const navEl = e.target.closest('.nav-scroll');
38
+
39
+ const elList = [...navEl.querySelectorAll('.nav-bar-tag-and-paths')];
40
+ if (action === 'expand-all') {
41
+ elList.map((el) => { expandCollapseNavBarTag(el, 'expand'); });
42
+ this.resolvedSpec.tags.forEach(t => { t.expanded = true; });
43
+ this.operationsCollapsed = false;
44
+ } else {
45
+ elList.map((el) => { expandCollapseNavBarTag(el, 'collapse'); });
46
+ this.resolvedSpec.tags.forEach(t => { t.expanded = false; });
47
+ this.operationsCollapsed = true;
48
+ }
37
49
  }
38
50
 
39
51
  /* eslint-disable indent */
@@ -96,17 +108,19 @@ export default function navbarTemplate() {
96
108
  <slot name="nav-section" class="custom-nav-section" data-content-id='section' @click = '${(e) => this.scrollToEventTarget(e, false)}'></slot>
97
109
 
98
110
  <slot name="operations-header">
99
- <div id='link-paths' class='nav-bar-section' part="navbar-operations-header">
100
- <div class='nav-bar-section-title'>OPERATIONS</div>
101
- <div style="display:flex; margin-left:10px;">
102
- ${this.renderStyle === 'focused' && this.resolvedSpec.tags.length > 1
103
- ? html`
104
- ${this.operationsCollapsed
105
- ? html`<div @click="${(e) => { onExpandCollapseAll.call(this, e, 'expand-all'); this.operationsCollapsed = false; }}" style="font-size: 16px; transform: rotate(0deg); cursor: pointer;">▸</div>`
106
- : html`<div @click="${(e) => { onExpandCollapseAll.call(this, e, 'collapse-all'); this.operationsCollapsed = true; }}" style="font-size: 16px; transform: rotate(90deg); cursor: pointer;">▸</div>`
107
- }`
108
- : ''
109
- }
111
+ <div class="sticky-scroll-element">
112
+ <div id='link-paths' class='nav-bar-section' part="navbar-operations-header">
113
+ <div class='nav-bar-section-title'>OPERATIONS</div>
114
+ <div style="display:flex; margin-left:10px;">
115
+ ${this.renderStyle === 'focused' && this.resolvedSpec.tags.length > 1
116
+ ? html`
117
+ ${this.operationsCollapsed
118
+ ? html`<div @click="${(e) => { expandCollapseAll.call(this, e, 'expand-all'); }}" style="font-size: 16px; transform: rotate(0deg); cursor: pointer;">▸</div>`
119
+ : html`<div @click="${(e) => { expandCollapseAll.call(this, e, 'collapse-all'); }}" style="font-size: 16px; transform: rotate(90deg); cursor: pointer;">▸</div>`
120
+ }`
121
+ : ''
122
+ }
123
+ </div>
110
124
  </div>
111
125
  </div>
112
126
  </slot>
@@ -118,25 +132,15 @@ export default function navbarTemplate() {
118
132
  <slot name="nav-${tag.elementId}">
119
133
  <div class='nav-bar-tag-and-paths ${tag.expanded ? 'expanded' : 'collapsed'}'>
120
134
  ${tag.name === 'General ⦂'
121
- ? html`<hr style="border:none; border-top: 1px dotted var(--nav-text-color); opacity:0.4; margin-top:-1px;"/>`
135
+ ? html``
122
136
  : html`
123
- <div
124
- class='nav-bar-tag'
125
- id="link-${tag.elementId}"
126
- data-content-id='${tag.elementId}'
127
- @click='${(e) => {
128
- if (this.renderStyle === 'focused' && this.onNavTagClick === 'expand-collapse') {
129
- onExpandCollapse.call(this, e);
130
- } else {
131
- this.scrollToEventTarget(e, false);
132
- }
133
- }}'>
134
- <div>${tag.name}</div>
135
- <div class="nav-bar-tag-icon" @click="${(e) => {
136
- if (this.renderStyle === 'focused' && this.onNavTagClick === 'show-description') {
137
- onExpandCollapse.call(this, e);
138
- }
139
- }}">
137
+ <div class='nav-bar-tag' id="link-${tag.elementId}" data-content-id='${tag.elementId}'
138
+ @click='${(e) => { onExpandCollapse.call(this, tag.elementId, e); }}'>
139
+
140
+ <div style="display: flex; justify-content: space-between; width: 100%;">
141
+ <div>${tag.name}</div>
142
+ <div class="nav-bar-tag-icon expand-button-arrow">▸</div>
143
+ <div class="nav-bar-tag-icon collapse-button-arrow">▾</div>
140
144
  </div>
141
145
  </div>
142
146
  `
@@ -170,8 +174,10 @@ export default function navbarTemplate() {
170
174
  <!-- COMPONENTS -->
171
175
  ${this.resolvedSpec.components && this.showComponents === 'true'
172
176
  ? html`
173
- <div id='link-components' class='nav-bar-section'>
174
- <div class='nav-bar-section-title'>COMPONENTS</div>
177
+ <div class="sticky-scroll-element">
178
+ <div id='link-components' class='nav-bar-section'>
179
+ <div class='nav-bar-section-title'>COMPONENTS</div>
180
+ </div>
175
181
  </div>
176
182
  ${this.resolvedSpec.components.map((component) => (component.subComponents.length
177
183
  ? html`
@@ -270,7 +270,7 @@ function oAuthFlowTemplate(flowName, securityObj, authFlow) {
270
270
  ${flowName === 'clientCredentials'
271
271
  ? html`
272
272
  <input type="password" part="textbox textbox-auth-client-secret" value = "" placeholder="Client Secret" spellcheck="false" class="oauth-client-secret" style = "margin:0 5px;">
273
- <select style="margin-right:5px;" class="oauth-send-client-secret-in">
273
+ <select aria-label='oauth client secret location' style="margin-right:5px;" class="oauth-send-client-secret-in">
274
274
  <option value = 'header' selected> Authorization Header </option>
275
275
  <option value = 'request-body'> Request Body </option>
276
276
  </select>`
@@ -314,7 +314,7 @@ export default function securitySchemeTemplate() {
314
314
  </div>
315
315
  ${schemes.length > 0
316
316
  ? html`
317
- <table class='m-table' style = "width:100%">
317
+ <table role="presentation" class='m-table' style="width:100%">
318
318
  ${schemes.map((v) => html`
319
319
  <tr>
320
320
  <td style="max-width:500px; overflow-wrap: break-word;">
@@ -26,7 +26,7 @@ function serverVarsTemplate() {
26
26
  return this.selectedServer && this.selectedServer.variables
27
27
  ? html`
28
28
  <div class="table-title"> SERVER VARIABLES</div>
29
- <table class='m-table'>
29
+ <table role="presentation" class='m-table'>
30
30
  ${Object.entries(this.selectedServer.variables).map((kv) => html`
31
31
  <tr>
32
32
  <td style="vertical-align: middle;" >${kv[0]}</td>
@@ -3,7 +3,8 @@ import { marked } from 'marked';
3
3
  import yaml from 'js-yaml';
4
4
  import { invalidCharsRegEx } from './common-utils';
5
5
 
6
- export default async function ProcessSpec(requiresLookup, specUrlOrObject, serverUrl = '') {
6
+ export default async function ProcessSpec(specUrlOrObject, serverUrl = '') {
7
+ const inputSpecIsAUrl = typeof specUrlOrObject === 'string' && specUrlOrObject.match(/^http/);
7
8
  let specMeta;
8
9
 
9
10
  // Dynamically resolve non yaml or json files and insert their descriptions where necessary
@@ -14,7 +15,7 @@ export default async function ProcessSpec(requiresLookup, specUrlOrObject, serve
14
15
  return val;
15
16
  }
16
17
 
17
- if (requiresLookup) {
18
+ if (inputSpecIsAUrl) {
18
19
  specMeta = await SwaggerClient({ allowMetaPatches: false, url: specUrlOrObject, responseInterceptor });
19
20
  } else if (typeof specUrlOrObject === 'string') {
20
21
  specMeta = await SwaggerClient({ allowMetaPatches: false, spec: yaml.load(specUrlOrObject), responseInterceptor });
@@ -71,7 +72,7 @@ export default async function ProcessSpec(requiresLookup, specUrlOrObject, serve
71
72
 
72
73
  // Servers
73
74
  let servers = [];
74
- if (jsonParsedSpec.servers && Array.isArray(jsonParsedSpec.servers) && jsonParsedSpec.servers.length) {
75
+ if (Array.isArray(jsonParsedSpec.servers) && jsonParsedSpec.servers.length) {
75
76
  jsonParsedSpec.servers.forEach((v) => {
76
77
  let computedUrl = v.url.trim();
77
78
  if (!(computedUrl.startsWith('http') || computedUrl.startsWith('//') || computedUrl.startsWith('{'))) {
@@ -90,11 +91,13 @@ export default async function ProcessSpec(requiresLookup, specUrlOrObject, serve
90
91
  }
91
92
  v.computedUrl = computedUrl;
92
93
  });
93
- if (serverUrl) {
94
+ if (serverUrl && !jsonParsedSpec.servers.some(s => s.url === serverUrl || s.computedUrl === serverUrl)) {
94
95
  jsonParsedSpec.servers.push({ url: serverUrl, computedUrl: serverUrl });
95
96
  }
96
97
  } else if (serverUrl) {
97
98
  jsonParsedSpec.servers = [{ url: serverUrl, computedUrl: serverUrl }];
99
+ } else if (inputSpecIsAUrl) {
100
+ jsonParsedSpec.servers = [{ url: new URL(specUrlOrObject).origin, computedUrl: new URL(specUrlOrObject).origin }];
98
101
  } else if (window.location.origin.startsWith('http')) {
99
102
  jsonParsedSpec.servers = [{ url: window.location.origin, computedUrl: window.location.origin }];
100
103
  } else {