openapi-explorer 0.9.310 → 0.9.314
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/dist/{openapi-explorer.min.js → browser/openapi-explorer.min.js} +3 -3
- package/dist/{openapi-explorer.min.js.map → browser/openapi-explorer.min.js.map} +0 -0
- package/dist/es/components/api-request.js +936 -0
- package/dist/es/components/api-response.js +184 -0
- package/dist/es/components/json-tree.js +67 -0
- package/{src → dist/es}/components/mime-types.js +11 -17
- package/dist/es/components/schema-table.js +156 -0
- package/dist/es/components/schema-tree.js +191 -0
- package/dist/es/components/tag-input.js +67 -0
- package/{src → dist/es}/openapi-explorer-oauth-handler.js +2 -2
- package/{src → dist/es}/openapi-explorer.js +364 -371
- package/dist/es/styles/advanced-search-styles.js +2 -0
- package/dist/es/styles/api-request-styles.js +2 -0
- package/dist/es/styles/border-styles.js +2 -0
- package/dist/es/styles/endpoint-styles.js +2 -0
- package/dist/es/styles/flex-styles.js +2 -0
- package/dist/es/styles/font-styles.js +2 -0
- package/dist/es/styles/info-styles.js +2 -0
- package/dist/es/styles/input-styles.js +4 -0
- package/dist/es/styles/nav-styles.js +2 -0
- package/dist/es/styles/prism-styles.js +2 -0
- package/dist/es/styles/schema-styles.js +2 -0
- package/dist/es/styles/tab-styles.js +2 -0
- package/dist/es/styles/table-styles.js +2 -0
- package/dist/es/templates/advance-search-template.js +37 -0
- package/dist/es/templates/callback-template.js +7 -0
- package/dist/es/templates/code-samples-template.js +26 -0
- package/dist/es/templates/components-template.js +17 -0
- package/dist/es/templates/endpoint-template.js +94 -0
- package/dist/es/templates/expanded-endpoint-template.js +32 -0
- package/{src → dist/es}/templates/focused-endpoint-template.js +15 -15
- package/dist/es/templates/navbar-template.js +46 -0
- package/dist/es/templates/overview-template.js +9 -0
- package/dist/es/templates/responsiveViewMainBodyTemplate.js +30 -0
- package/dist/es/templates/security-scheme-template.js +330 -0
- package/dist/es/templates/server-template.js +42 -0
- package/{src → dist/es}/utils/color-utils.js +53 -16
- package/{src → dist/es}/utils/common-utils.js +18 -18
- package/{src → dist/es}/utils/schema-utils.js +248 -124
- package/{src → dist/es}/utils/spec-parser.js +112 -71
- package/dist/es/utils/theme.js +75 -0
- package/{src → dist/es}/utils/xml/xml.js +41 -38
- package/dist/lib/components/api-request.js +957 -0
- package/dist/lib/components/api-response.js +206 -0
- package/dist/lib/components/json-tree.js +82 -0
- package/dist/lib/components/mime-types.js +70 -0
- package/dist/lib/components/schema-table.js +170 -0
- package/dist/lib/components/schema-tree.js +206 -0
- package/dist/lib/components/tag-input.js +76 -0
- package/dist/lib/openapi-explorer-oauth-handler.js +19 -0
- package/dist/lib/openapi-explorer.js +817 -0
- package/dist/lib/styles/advanced-search-styles.js +10 -0
- package/dist/lib/styles/api-request-styles.js +10 -0
- package/dist/lib/styles/border-styles.js +10 -0
- package/dist/lib/styles/endpoint-styles.js +10 -0
- package/dist/lib/styles/flex-styles.js +10 -0
- package/dist/lib/styles/font-styles.js +10 -0
- package/dist/lib/styles/info-styles.js +10 -0
- package/dist/lib/styles/input-styles.js +11 -0
- package/dist/lib/styles/nav-styles.js +10 -0
- package/dist/lib/styles/prism-styles.js +10 -0
- package/dist/lib/styles/schema-styles.js +10 -0
- package/dist/lib/styles/tab-styles.js +10 -0
- package/dist/lib/styles/table-styles.js +10 -0
- package/dist/lib/templates/advance-search-template.js +42 -0
- package/dist/lib/templates/callback-template.js +12 -0
- package/dist/lib/templates/code-samples-template.js +36 -0
- package/dist/lib/templates/components-template.js +27 -0
- package/dist/lib/templates/endpoint-template.js +111 -0
- package/dist/lib/templates/expanded-endpoint-template.js +48 -0
- package/dist/lib/templates/focused-endpoint-template.js +95 -0
- package/dist/lib/templates/navbar-template.js +54 -0
- package/dist/lib/templates/overview-template.js +16 -0
- package/dist/lib/templates/responsiveViewMainBodyTemplate.js +47 -0
- package/dist/lib/templates/security-scheme-template.js +342 -0
- package/dist/lib/templates/server-template.js +49 -0
- package/dist/lib/utils/color-utils.js +112 -0
- package/dist/lib/utils/common-utils.js +156 -0
- package/dist/lib/utils/schema-utils.js +743 -0
- package/dist/lib/utils/spec-parser.js +361 -0
- package/dist/lib/utils/theme.js +84 -0
- package/dist/lib/utils/xml/xml.js +239 -0
- package/package.json +19 -6
- package/dist/openapi-explorer.min.js.LICENSE.txt +0 -71
- 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.gz +0 -0
- package/dist/report.html +0 -38
- package/src/components/api-request.js +0 -1244
- package/src/components/api-response.js +0 -340
- package/src/components/json-tree.js +0 -129
- package/src/components/schema-table.js +0 -250
- package/src/components/schema-tree.js +0 -280
- package/src/components/tag-input.js +0 -109
- package/src/styles/advanced-search-styles.js +0 -84
- package/src/styles/api-request-styles.js +0 -111
- package/src/styles/border-styles.js +0 -24
- package/src/styles/css/main.css +0 -24
- package/src/styles/endpoint-styles.js +0 -222
- package/src/styles/flex-styles.js +0 -15
- package/src/styles/font-styles.js +0 -266
- package/src/styles/info-styles.js +0 -20
- package/src/styles/input-styles.js +0 -236
- package/src/styles/nav-styles.js +0 -141
- package/src/styles/prism-styles.js +0 -107
- package/src/styles/schema-styles.js +0 -121
- package/src/styles/tab-styles.js +0 -44
- package/src/styles/table-styles.js +0 -48
- package/src/templates/advance-search-template.js +0 -81
- package/src/templates/callback-template.js +0 -63
- package/src/templates/code-samples-template.js +0 -35
- package/src/templates/components-template.js +0 -43
- package/src/templates/endpoint-template.js +0 -175
- package/src/templates/expanded-endpoint-template.js +0 -104
- package/src/templates/navbar-template.js +0 -175
- package/src/templates/overview-template.js +0 -58
- package/src/templates/responsiveViewMainBodyTemplate.js +0 -72
- package/src/templates/security-scheme-template.js +0 -487
- package/src/templates/server-template.js +0 -106
- package/src/utils/theme.js +0 -163
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.default = navbarTemplate;
|
|
5
|
+
exports.expandCollapseAll = expandCollapseAll;
|
|
6
|
+
|
|
7
|
+
var _litElement = require("lit-element");
|
|
8
|
+
|
|
9
|
+
var _commonUtils = require("../utils/common-utils");
|
|
10
|
+
|
|
11
|
+
function onExpandCollapse(tagId) {
|
|
12
|
+
const tag = this.resolvedSpec.tags.find(t => t.elementId === tagId);
|
|
13
|
+
|
|
14
|
+
if (!tag) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
tag.expanded = !tag.expanded;
|
|
19
|
+
|
|
20
|
+
if (tag.expanded && this.operationsCollapsed) {
|
|
21
|
+
this.resolvedSpec.tags.filter(t => t.elementId !== tagId).forEach(t => t.expanded = false);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
this.requestUpdate();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function expandCollapseAll() {
|
|
28
|
+
const expand = this.operationsCollapsed;
|
|
29
|
+
this.operationsCollapsed = !expand;
|
|
30
|
+
this.resolvedSpec.tags.forEach(t => {
|
|
31
|
+
t.expanded = expand;
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/* eslint-disable indent */
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
function navbarTemplate() {
|
|
38
|
+
return (0, _litElement.html)` <nav class="nav-bar ${this.renderStyle}" part="section-navbar"> ${this.allowSearch === 'false' && this.allowAdvancedSearch === 'false' ? '' : (0, _litElement.html)` <div style="display:flex;flex-direction:row;justify-content:center;align-items:center;padding:24px;${this.allowAdvancedSearch === 'false' ? 'border-bottom: 1px solid var(--nav-hover-bg-color)' : ''}"> ${this.allowSearch === 'false' ? '' : (0, _litElement.html)` <div style="display:flex;flex:1;line-height:22px"> <input id="nav-bar-search" part="textbox textbox-nav-filter" style="width:100%;padding-right:20px;color:var(--nav-hover-text-color);border-color:var(--secondary-color);background-color:var(--nav-hover-bg-color)" type="text" placeholder="Filter" @change="${this.onSearchChange}" spellcheck="false"> <div style="margin:6px 5px 0 -24px;font-size:var(--font-size-regular);cursor:pointer">↩</div> </div> ${this.matchPaths ? (0, _litElement.html)` <div @click="${this.onClearSearch}" style="margin-left:5px;cursor:pointer;align-self:center;color:var(--nav-text-color)" class="small-font-size primary-text bold-text"> CLEAR </div>` : ''} `} ${this.allowAdvancedSearch === 'false' || this.matchPaths ? '' : (0, _litElement.html)` <button class="m-btn primary" part="btn btn-fill btn-search" style="margin-left:5px" @click="${this.onShowSearchModalClicked}"> Search </button> `} </div> `} ${(0, _litElement.html)`<nav class="nav-scroll" part="navbar-scroll"> ${this.showInfo === 'false' || !this.resolvedSpec.info ? '' : (0, _litElement.html)`<div class="nav-bar-info" id="link-overview" data-content-id="overview" @click="${e => this.scrollToEventTarget(e, false)}"> ${this.isV1 && this.resolvedSpec.info.title || 'Overview'} </div>`} ${this.allowServerSelection === 'false' ? '' : (0, _litElement.html)`<div class="nav-bar-info" id="link-servers" data-content-id="servers" @click="${e => this.scrollToEventTarget(e, false)}"> API Servers </div>`} ${this.allowAuthentication === 'false' || !this.resolvedSpec.securitySchemes ? '' : (0, _litElement.html)`<div class="nav-bar-info" id="link-auth" data-content-id="auth" @click="${e => this.scrollToEventTarget(e, false)}"> Authentication </div>`} <slot name="nav-section" class="custom-nav-section" data-content-id="section" @click="${e => this.scrollToEventTarget(e, false)}"></slot> <div class="sticky-scroll-element"> <div class="nav-bar-section" part="navbar-operations-header"> <slot name="operations-header"> <div class="nav-bar-section-title">OPERATIONS</div> </slot> <div style="" part="navbar-operations-header-collapse"> ${this.resolvedSpec.tags.length > 1 ? (0, _litElement.html)` ${this.operationsCollapsed ? (0, _litElement.html)`<div @click="${e => {
|
|
39
|
+
expandCollapseAll.call(this, e);
|
|
40
|
+
}}" style="font-size:16px;transform:rotate(0);cursor:pointer">▸</div>` : (0, _litElement.html)`<div @click="${e => {
|
|
41
|
+
expandCollapseAll.call(this, e);
|
|
42
|
+
}}" style="font-size:16px;transform:rotate(90deg);cursor:pointer">▸</div>`}` : ''} </div> </div> </div> ${this.resolvedSpec.tags.filter(tag => tag.paths.filter(path => (0, _commonUtils.pathIsInSearch)(this.matchPaths, path)).length).map(tag => (0, _litElement.html)` <slot name="nav-${tag.elementId}"> <div class="nav-bar-tag-and-paths ${tag.expanded ? 'expanded' : 'collapsed'}"> ${tag.name === 'General ⦂' ? (0, _litElement.html)`` : (0, _litElement.html)` <div class="nav-bar-tag" id="link-${tag.elementId}" data-content-id="${tag.elementId}" @click="${() => {
|
|
43
|
+
onExpandCollapse.call(this, tag.elementId);
|
|
44
|
+
}}"> <div style="display:flex;justify-content:space-between;width:100%"> <div>${tag.name}</div> <div class="nav-bar-tag-icon expand-button-arrow">▸</div> <div class="nav-bar-tag-icon collapse-button-arrow">▾</div> </div> </div> `} <div class="nav-bar-paths-under-tag"> ${tag.paths.filter(v => {
|
|
45
|
+
if (this.matchPaths) {
|
|
46
|
+
return (0, _commonUtils.pathIsInSearch)(this.matchPaths, v);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return true;
|
|
50
|
+
}).map(p => (0, _litElement.html)` <div class="nav-bar-path ${this.usePathInNavBar === 'true' ? 'small-font' : ''}" data-content-id="${p.elementId}" id="link-${p.elementId}" @click="${e => {
|
|
51
|
+
this.scrollToEventTarget(e, false);
|
|
52
|
+
}}"> <span style="line-break:anywhere;${p.deprecated ? 'filter:opacity(0.5)' : ''}"> ${this.usePathInNavBar === 'true' ? (0, _litElement.html)`<span class="mono-font">${p.method.toUpperCase()} ${p.path}</span>` : p.summary || p.shortSummary} ${p.isWebhook ? '(Webhook)' : ''} </span> </div>`)} </div> </div> </slot> `)} ${this.resolvedSpec.components && this.showComponents === 'true' ? (0, _litElement.html)` <div class="sticky-scroll-element"> <div id="link-components" class="nav-bar-section"> <div class="nav-bar-section-title">COMPONENTS</div> </div> </div> ${this.resolvedSpec.components.map(component => component.subComponents.filter(s => s.expanded).length ? (0, _litElement.html)` <div class="nav-bar-tag" data-content-id="cmp--${component.name.toLowerCase()}" id="link-cmp--${component.name.toLowerCase()}" @click="${e => this.scrollToEventTarget(e, false)}"> ${component.name} </div> ${component.subComponents.filter(s => s.expanded).map(p => (0, _litElement.html)` <div class="nav-bar-path" data-content-id="cmp--${p.id}" id="link-cmp--${p.id}" @click="${e => this.scrollToEventTarget(e, false)}"> <span> ${p.name} </span> </div>`)}` : '')}` : ''} </nav>`} </nav> `;
|
|
53
|
+
}
|
|
54
|
+
/* eslint-enable indent */
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.default = overviewTemplate;
|
|
5
|
+
|
|
6
|
+
var _litElement = require("lit-element");
|
|
7
|
+
|
|
8
|
+
var _unsafeHtml = require("lit-html/directives/unsafe-html");
|
|
9
|
+
|
|
10
|
+
var _marked = require("marked");
|
|
11
|
+
|
|
12
|
+
/* eslint-disable indent */
|
|
13
|
+
function overviewTemplate() {
|
|
14
|
+
return (0, _litElement.html)` <section id="overview" part="section-overview" class="observe-me ${this.renderStyle === 'focused' ? 'section-gap--focused-mode' : 'section-gap'}"> ${this.resolvedSpec && this.resolvedSpec.info ? (0, _litElement.html)` <slot name="overview"> <div id="api-title" part="label-overview-title" style="font-size:32px" class="section-padding"> ${this.resolvedSpec.info.title} ${!this.resolvedSpec.info.version ? '' : (0, _litElement.html)` <span style="font-size:var(--font-size-small);font-weight:700"> ${this.resolvedSpec.info.version} </span>`} </div> <div id="api-info" style="font-size:calc(var(--font-size-regular) - 1px);margin-top:8px" class="section-padding"> ${this.resolvedSpec.info.contact && this.resolvedSpec.info.contact.email ? (0, _litElement.html)`<span>${this.resolvedSpec.info.contact.name || 'Email'}: <a href="mailto:${this.resolvedSpec.info.contact.email}" part="anchor anchor-overview">${this.resolvedSpec.info.contact.email}</a> </span>` : ''} ${this.resolvedSpec.info.contact && this.resolvedSpec.info.contact.url ? (0, _litElement.html)`<span>URL: <a href="${this.resolvedSpec.info.contact.url}" part="anchor anchor-overview">${this.resolvedSpec.info.contact.url}</a></span>` : ''} ${this.resolvedSpec.info.license ? (0, _litElement.html)`<span>License: ${this.resolvedSpec.info.license.url ? (0, _litElement.html)`<a href="${this.resolvedSpec.info.license.url}" part="anchor anchor-overview">${this.resolvedSpec.info.license.name}</a>` : this.resolvedSpec.info.license.name} </span>` : ''} ${this.resolvedSpec.info.termsOfService ? (0, _litElement.html)`<span><a href="${this.resolvedSpec.info.termsOfService}" part="anchor anchor-overview">Terms of Service</a></span>` : ''} </div> </slot> <slot name="overview-api-description"> ${this.resolvedSpec.info.description ? (0, _litElement.html)`${(0, _unsafeHtml.unsafeHTML)(`<div class="m-markdown regular-font section-padding">${(0, _marked.marked)(this.resolvedSpec.info.description)}</div>`)}` : ''} </slot> ` : ''} </section> `;
|
|
15
|
+
}
|
|
16
|
+
/* eslint-enable indent */
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.default = responsiveViewMainBodyTemplate;
|
|
5
|
+
|
|
6
|
+
var _litElement = require("lit-element");
|
|
7
|
+
|
|
8
|
+
var _focusedEndpointTemplate = _interopRequireDefault(require("./focused-endpoint-template"));
|
|
9
|
+
|
|
10
|
+
var _overviewTemplate = _interopRequireDefault(require("./overview-template"));
|
|
11
|
+
|
|
12
|
+
var _endpointTemplate = _interopRequireDefault(require("./endpoint-template"));
|
|
13
|
+
|
|
14
|
+
var _serverTemplate = _interopRequireDefault(require("./server-template"));
|
|
15
|
+
|
|
16
|
+
var _securitySchemeTemplate = _interopRequireDefault(require("./security-scheme-template"));
|
|
17
|
+
|
|
18
|
+
var _navbarTemplate = _interopRequireDefault(require("./navbar-template"));
|
|
19
|
+
|
|
20
|
+
var _advanceSearchTemplate = _interopRequireDefault(require("./advance-search-template"));
|
|
21
|
+
|
|
22
|
+
var _theme = _interopRequireDefault(require("../utils/theme"));
|
|
23
|
+
|
|
24
|
+
var _colorUtils = _interopRequireDefault(require("../utils/color-utils"));
|
|
25
|
+
|
|
26
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
27
|
+
|
|
28
|
+
// Templates
|
|
29
|
+
function responsiveViewMainBodyTemplate() {
|
|
30
|
+
const newTheme = {
|
|
31
|
+
bg1: _colorUtils.default.isValidHexColor(this.bgColor) ? this.bgColor : '',
|
|
32
|
+
fg1: _colorUtils.default.isValidHexColor(this.textColor) ? this.textColor : '',
|
|
33
|
+
primaryColor: _colorUtils.default.isValidHexColor(this.primaryColor) ? this.primaryColor : '#3E6077',
|
|
34
|
+
secondaryColor: _colorUtils.default.isValidHexColor(this.secondaryColor) ? this.secondaryColor : '#FBAF0B',
|
|
35
|
+
headerColor: _colorUtils.default.isValidHexColor(this.headerColor) ? this.headerColor : '',
|
|
36
|
+
navBgColor: _colorUtils.default.isValidHexColor(this.navBgColor) ? this.navBgColor : '',
|
|
37
|
+
navTextColor: _colorUtils.default.isValidHexColor(this.navTextColor) ? this.navTextColor : '',
|
|
38
|
+
navHoverBgColor: _colorUtils.default.isValidHexColor(this.navHoverBgColor) ? this.navHoverBgColor : '',
|
|
39
|
+
navHoverTextColor: _colorUtils.default.isValidHexColor(this.navHoverTextColor) ? this.navHoverTextColor : ''
|
|
40
|
+
};
|
|
41
|
+
/* eslint-disable indent */
|
|
42
|
+
|
|
43
|
+
return (0, _litElement.html)` ${_theme.default.call(this, newTheme)} ${this.allowAdvancedSearch === 'false' ? '' : _advanceSearchTemplate.default.call(this)} <div id="the-main-body" class="body"> ${this.renderStyle === 'focused' && this.resolvedSpec ? _navbarTemplate.default.call(this) : ''} ${this.loading === true ? (0, _litElement.html)`<slot name="loader"><div class="loader"></div></slot>` : (0, _litElement.html)` <main class="main-content regular-font" part="section-main-content"> <slot></slot> <div id="operations-root" class="main-content-inner"> ${this.loadFailed === true ? (0, _litElement.html)`<div style="text-align:center;margin:16px">Unable to load the Spec${this.specUrl ? ': ' : ''}<strong>${this.specUrl}</strong></div>` : (0, _litElement.html)` <div class="operations-root" @click="${e => {
|
|
44
|
+
this.handleHref(e);
|
|
45
|
+
}}"> ${this.renderStyle === 'focused' ? (0, _litElement.html)`${_focusedEndpointTemplate.default.call(this)}` : (0, _litElement.html)` ${this.showInfo === 'true' ? _overviewTemplate.default.call(this) : ''} ${this.allowServerSelection === 'true' ? _serverTemplate.default.call(this) : ''} ${this.allowAuthentication === 'true' ? _securitySchemeTemplate.default.call(this) : ''} <section id="section" class="observe-me ${this.renderStyle === 'focused' ? 'section-gap--focused-mode' : 'section-gap'}"> <slot name="custom-section"></slot> </section> ${_endpointTemplate.default.call(this)}`} </div> `}</div></main>`} </div> <slot name="footer"></slot> `;
|
|
46
|
+
}
|
|
47
|
+
/* eslint-enable indent */
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.checkForAuthToken = checkForAuthToken;
|
|
5
|
+
exports.default = securitySchemeTemplate;
|
|
6
|
+
exports.pathSecurityTemplate = pathSecurityTemplate;
|
|
7
|
+
|
|
8
|
+
var _litElement = require("lit-element");
|
|
9
|
+
|
|
10
|
+
var _unsafeHtml = require("lit-html/directives/unsafe-html");
|
|
11
|
+
|
|
12
|
+
var _marked = require("marked");
|
|
13
|
+
|
|
14
|
+
var _base64url = _interopRequireDefault(require("base64url"));
|
|
15
|
+
|
|
16
|
+
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
17
|
+
|
|
18
|
+
function onApiKeyChange(apiKeyId, e) {
|
|
19
|
+
let apiKeyValue = '';
|
|
20
|
+
const securityObj = this.resolvedSpec.securitySchemes.find(v => v.apiKeyId === apiKeyId);
|
|
21
|
+
|
|
22
|
+
if (!securityObj) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const trEl = e.target.closest('tr');
|
|
27
|
+
|
|
28
|
+
if (securityObj.type && securityObj.type === 'http' && securityObj.scheme && securityObj.scheme.toLowerCase() === 'basic') {
|
|
29
|
+
const userVal = trEl.querySelector('.api-key-user').value.trim();
|
|
30
|
+
const passwordVal = trEl.querySelector('.api-key-password').value.trim();
|
|
31
|
+
|
|
32
|
+
if (passwordVal) {
|
|
33
|
+
apiKeyValue = `Basic ${btoa(`${userVal}:${passwordVal}`)}`;
|
|
34
|
+
}
|
|
35
|
+
} else {
|
|
36
|
+
apiKeyValue = trEl.querySelector('.api-key-input').value.trim();
|
|
37
|
+
|
|
38
|
+
if (apiKeyValue) {
|
|
39
|
+
if (securityObj.scheme && securityObj.scheme.toLowerCase() === 'bearer') {
|
|
40
|
+
apiKeyValue = `Bearer ${apiKeyValue.replace(/^Bearer\s+/i, '')}`;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
securityObj.finalKeyValue = apiKeyValue;
|
|
46
|
+
this.requestUpdate();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function onClearAllApiKeys() {
|
|
50
|
+
this.resolvedSpec.securitySchemes.forEach(v => {
|
|
51
|
+
v.user = '';
|
|
52
|
+
v.password = '';
|
|
53
|
+
v.value = '';
|
|
54
|
+
v.finalKeyValue = '';
|
|
55
|
+
});
|
|
56
|
+
this.requestUpdate();
|
|
57
|
+
} // Updates the OAuth Access Token (API key), so it reflects in UI and gets used in TRY calls
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
function updateOAuthKey(apiKeyId, tokenType = 'Bearer', accessToken) {
|
|
61
|
+
const securityObj = this.resolvedSpec.securitySchemes.find(v => v.apiKeyId === apiKeyId);
|
|
62
|
+
const tokenPrefix = tokenType && tokenType.toLowerCase() === 'bearer' ? 'Bearer' : tokenType;
|
|
63
|
+
securityObj.finalKeyValue = `${tokenPrefix}${tokenPrefix ? ' ' : ''}${accessToken}`;
|
|
64
|
+
this.requestUpdate();
|
|
65
|
+
} // Gets Access-Token in exchange of Authorization Code
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
async function fetchAccessToken(tokenUrl, clientId, clientSecret, redirectUrl, grantType, authCode, sendClientSecretIn = 'header', apiKeyId, authFlowDivEl, scopes = null) {
|
|
69
|
+
const respDisplayEl = authFlowDivEl ? authFlowDivEl.querySelector('.oauth-resp-display') : undefined;
|
|
70
|
+
const urlFormParams = new URLSearchParams();
|
|
71
|
+
const headers = new Headers();
|
|
72
|
+
urlFormParams.append('grant_type', grantType);
|
|
73
|
+
|
|
74
|
+
if (redirectUrl) {
|
|
75
|
+
urlFormParams.append('redirect_uri', redirectUrl);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (authCode) {
|
|
79
|
+
urlFormParams.append('code', authCode);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (sendClientSecretIn === 'header') {
|
|
83
|
+
headers.set('Authorization', `Basic ${btoa(`${clientId}:${clientSecret}`)}`);
|
|
84
|
+
} else {
|
|
85
|
+
urlFormParams.append('client_id', clientId);
|
|
86
|
+
|
|
87
|
+
if (clientSecret) {
|
|
88
|
+
urlFormParams.append('client_secret', clientSecret);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (scopes) {
|
|
93
|
+
urlFormParams.append('scope', scopes);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const {
|
|
97
|
+
codeVerifier
|
|
98
|
+
} = JSON.parse(localStorage.getItem('openapi-explorer-oauth') || '{}');
|
|
99
|
+
localStorage.removeItem('openapi-explorer-oauth');
|
|
100
|
+
|
|
101
|
+
if (codeVerifier) {
|
|
102
|
+
urlFormParams.append('code_verifier', codeVerifier);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
try {
|
|
106
|
+
const resp = await fetch(tokenUrl, {
|
|
107
|
+
method: 'POST',
|
|
108
|
+
headers,
|
|
109
|
+
body: urlFormParams
|
|
110
|
+
});
|
|
111
|
+
const tokenResp = await resp.json();
|
|
112
|
+
|
|
113
|
+
if (!resp.ok) {
|
|
114
|
+
if (respDisplayEl) {
|
|
115
|
+
respDisplayEl.innerHTML = `<span style="color:var(--red)">${tokenResp.error_description || tokenResp.error_description || 'Unable to get access token'}</span>`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (tokenResp.token_type && tokenResp.access_token) {
|
|
122
|
+
updateOAuthKey.call(this, apiKeyId, tokenResp.token_type, tokenResp.access_token);
|
|
123
|
+
|
|
124
|
+
if (respDisplayEl) {
|
|
125
|
+
respDisplayEl.innerHTML = '<span style="color:var(--green)">Access Token Received</span>';
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
} catch (err) {
|
|
129
|
+
if (respDisplayEl) {
|
|
130
|
+
respDisplayEl.innerHTML = '<span style="color:var(--red)">Failed to get access token</span>';
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function getCookieValue(keyId) {
|
|
136
|
+
const foundCookie = (document.cookie || '').split(';').find(c => c.split('=')[0] === keyId);
|
|
137
|
+
return foundCookie && foundCookie.split('=')[1] || '';
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function toObject(urlSearchParams) {
|
|
141
|
+
const result = {};
|
|
142
|
+
const entries = urlSearchParams && urlSearchParams.entries() || [];
|
|
143
|
+
|
|
144
|
+
for (const [key, value] of entries) {
|
|
145
|
+
result[key] = value;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
return result;
|
|
149
|
+
} // Gets invoked when it receives the Authorization Code from the other window via message-event
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
async function checkForAuthToken(redirectToApiLocation) {
|
|
153
|
+
const parameters = toObject(new URLSearchParams(window.location.search));
|
|
154
|
+
const hashQuery = toObject(new URLSearchParams(window.location.hash.slice(1)));
|
|
155
|
+
Object.assign(parameters, hashQuery);
|
|
156
|
+
const newUrl = new URL(window.location);
|
|
157
|
+
newUrl.searchParams.delete('nonce');
|
|
158
|
+
newUrl.searchParams.delete('expires_in');
|
|
159
|
+
newUrl.searchParams.delete('access_token');
|
|
160
|
+
newUrl.searchParams.delete('token_type');
|
|
161
|
+
newUrl.searchParams.delete('id_token');
|
|
162
|
+
newUrl.searchParams.delete('state');
|
|
163
|
+
newUrl.searchParams.delete('code');
|
|
164
|
+
newUrl.searchParams.delete('iss');
|
|
165
|
+
newUrl.searchParams.delete('scope');
|
|
166
|
+
newUrl.searchParams.delete('prompt');
|
|
167
|
+
newUrl.searchParams.delete('hd');
|
|
168
|
+
newUrl.searchParams.delete('authuser');
|
|
169
|
+
newUrl.searchParams.delete('redirect_auth');
|
|
170
|
+
|
|
171
|
+
if (!parameters.state) {
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const sanitizedUrlWithHash = newUrl.toString().replace(/#((code|state|access_token|id_token|authuser|expires_in|hd|prompt|scope|token_type)=[^&]+&?)*$/ig, '');
|
|
176
|
+
history.replaceState({}, undefined, sanitizedUrlWithHash);
|
|
177
|
+
const {
|
|
178
|
+
apiKeyId,
|
|
179
|
+
flowId,
|
|
180
|
+
url
|
|
181
|
+
} = JSON.parse(_base64url.default.decode(parameters.state));
|
|
182
|
+
|
|
183
|
+
if (redirectToApiLocation && url && !parameters.redirect_auth) {
|
|
184
|
+
const apiExplorerLocation = new URL(url);
|
|
185
|
+
Object.keys(parameters).forEach(key => apiExplorerLocation.searchParams.append(key, parameters[key]));
|
|
186
|
+
apiExplorerLocation.searchParams.append('redirect_auth', true);
|
|
187
|
+
window.location.replace(apiExplorerLocation.toString());
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (parameters.code) {
|
|
192
|
+
const securityObj = this.resolvedSpec.securitySchemes.find(v => v.apiKeyId === apiKeyId);
|
|
193
|
+
const tokenUrl = securityObj && securityObj.flows[flowId] && new URL(securityObj.flows[flowId].tokenUrl || '', this.selectedServer.computedUrl);
|
|
194
|
+
await fetchAccessToken.call(this, tokenUrl, securityObj.clientId, securityObj.clientSecret, securityObj.redirectUri || window.location.href, 'authorization_code', parameters.code, null, apiKeyId);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
updateOAuthKey.call(this, apiKeyId, parameters.token_type, parameters.access_token);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function onInvokeOAuthFlow(apiKeyId, flowType, authUrl, tokenUrl, e) {
|
|
202
|
+
const authFlowDivEl = e.target.closest('.oauth-flow');
|
|
203
|
+
const clientId = authFlowDivEl.querySelector('.oauth-client-id') ? authFlowDivEl.querySelector('.oauth-client-id').value.trim() : '';
|
|
204
|
+
const clientSecret = authFlowDivEl.querySelector('.oauth-client-secret') ? authFlowDivEl.querySelector('.oauth-client-secret').value.trim() : '';
|
|
205
|
+
const sendClientSecretIn = authFlowDivEl.querySelector('.oauth-send-client-secret-in') ? authFlowDivEl.querySelector('.oauth-send-client-secret-in').value.trim() : 'header';
|
|
206
|
+
const checkedScopeEls = [...authFlowDivEl.querySelectorAll('input[type="checkbox"]:checked')];
|
|
207
|
+
const securityObj = this.resolvedSpec.securitySchemes.find(v => v.apiKeyId === apiKeyId);
|
|
208
|
+
let grantType = '';
|
|
209
|
+
let responseType = ''; // clear previous error messages
|
|
210
|
+
|
|
211
|
+
const errEls = [...authFlowDivEl.parentNode.querySelectorAll('.oauth-resp-display')];
|
|
212
|
+
errEls.forEach(v => {
|
|
213
|
+
v.innerHTML = '';
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
if (flowType === 'authorizationCode' || flowType === 'implicit') {
|
|
217
|
+
const authUrlObj = new URL(authUrl);
|
|
218
|
+
const authCodeParams = new URLSearchParams(authUrlObj.search);
|
|
219
|
+
|
|
220
|
+
if (flowType === 'authorizationCode') {
|
|
221
|
+
const randomBytes = new Uint32Array(3);
|
|
222
|
+
(window.crypto || window.msCrypto).getRandomValues(randomBytes);
|
|
223
|
+
authCodeParams.set('nonce', randomBytes.toString('hex').split(',').join(''));
|
|
224
|
+
grantType = 'authorization_code';
|
|
225
|
+
responseType = 'code';
|
|
226
|
+
const codeVerifier = randomBytes.toString('hex').split(',').join('');
|
|
227
|
+
const hash = await (window.crypto || window.msCrypto).subtle.digest('SHA-256', new TextEncoder().encode(codeVerifier));
|
|
228
|
+
const codeChallenge = (0, _base64url.default)(hash);
|
|
229
|
+
authCodeParams.set('code_challenge', codeChallenge);
|
|
230
|
+
authCodeParams.set('code_challenge_method', 'S256');
|
|
231
|
+
localStorage.setItem('openapi-explorer-oauth', JSON.stringify({
|
|
232
|
+
codeVerifier
|
|
233
|
+
}));
|
|
234
|
+
} else if (flowType === 'implicit') {
|
|
235
|
+
responseType = 'token';
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const selectedScopes = checkedScopeEls.map(v => v.value).join(' ');
|
|
239
|
+
|
|
240
|
+
if (selectedScopes) {
|
|
241
|
+
authCodeParams.set('scope', selectedScopes);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
authCodeParams.set('client_id', clientId);
|
|
245
|
+
authCodeParams.set('redirect_uri', securityObj.redirectUri || window.location.href);
|
|
246
|
+
authCodeParams.set('response_type', responseType);
|
|
247
|
+
authCodeParams.set('state', _base64url.default.encode(JSON.stringify({
|
|
248
|
+
apiKeyId,
|
|
249
|
+
flowId: flowType,
|
|
250
|
+
url: window.location.href
|
|
251
|
+
})));
|
|
252
|
+
authUrlObj.search = authCodeParams.toString();
|
|
253
|
+
window.location.assign(authUrlObj.toString());
|
|
254
|
+
} else if (flowType === 'clientCredentials') {
|
|
255
|
+
grantType = 'client_credentials';
|
|
256
|
+
const selectedScopes = checkedScopeEls.map(v => v.value).join(' ');
|
|
257
|
+
fetchAccessToken.call(this, tokenUrl, clientId, clientSecret, '', grantType, '', sendClientSecretIn, apiKeyId, authFlowDivEl, selectedScopes);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/* eslint-disable indent */
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
function oAuthFlowTemplate(flowName, securityObj, authFlow) {
|
|
264
|
+
const apiKeyId = securityObj.apiKeyId;
|
|
265
|
+
|
|
266
|
+
const getFullUrl = url => url ? new URL(url, this.selectedServer.computedUrl) : undefined;
|
|
267
|
+
|
|
268
|
+
const authorizationUrl = getFullUrl(authFlow.authorizationUrl, this.selectedServer.computedUrl);
|
|
269
|
+
const tokenUrl = getFullUrl(authFlow.tokenUrl, this.selectedServer.computedUrl);
|
|
270
|
+
const refreshUrl = getFullUrl(authFlow.refreshUrl, this.selectedServer.computedUrl);
|
|
271
|
+
let flowNameDisplay;
|
|
272
|
+
|
|
273
|
+
if (flowName === 'authorizationCode') {
|
|
274
|
+
flowNameDisplay = 'Authorization Code Flow';
|
|
275
|
+
} else if (flowName === 'clientCredentials') {
|
|
276
|
+
flowNameDisplay = 'Client Credentials Flow';
|
|
277
|
+
} else if (flowName === 'implicit') {
|
|
278
|
+
flowNameDisplay = 'Implicit Flow';
|
|
279
|
+
} else {
|
|
280
|
+
flowNameDisplay = flowName;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return (0, _litElement.html)` <div class="oauth-flow" style="padding:10px 0;margin-bottom:10px"> <div class="tiny-title upper" style="margin-bottom:5px">${flowNameDisplay}</div> ${authorizationUrl ? (0, _litElement.html)`<div><span style="width:75px;display:inline-block">Auth URL</span> <span class="mono-font"> ${authorizationUrl} </span></div>` : ''} ${tokenUrl ? (0, _litElement.html)`<div><span style="width:75px;display:inline-block">Token URL</span> <span class="mono-font">${tokenUrl}</span></div>` : ''} ${refreshUrl ? (0, _litElement.html)`<div><span style="width:75px;display:inline-block">Refresh URL</span> <span class="mono-font">${refreshUrl}</span></div>` : ''} ${flowName === 'authorizationCode' || flowName === 'clientCredentials' || flowName === 'implicit' ? (0, _litElement.html)` ${authFlow.scopes ? (0, _litElement.html)` <span> Scopes </span> <div class="oauth-scopes" part="section-auth-scopes" style="width:100%;display:flex;flex-direction:column;flex-wrap:wrap;margin:0 0 .125rem 0"> ${Object.entries(authFlow.scopes).map((scopeAndDescr, index) => (0, _litElement.html)` <div class="m-checkbox" style="display:inline-flex;align-items:center"> <input type="checkbox" checked="checked" part="checkbox checkbox-auth-scope" id="${flowName}${index}" value="${scopeAndDescr[0]}"> <label for="${flowName}${index}" style="margin-left:5px"> <span class="mono-font">${scopeAndDescr[0]}</span> ${scopeAndDescr[0] !== scopeAndDescr[1] ? ` - ${scopeAndDescr[1] || ''}` : ''} </label> </div> `)} </div> ` : ''} <div style="display:flex"> <input type="text" part="textbox textbox-auth-client-id" value="${securityObj.clientId || ''}" placeholder="Client ID" spellcheck="false" class="oauth-client-id"> ${flowName === 'clientCredentials' ? (0, _litElement.html)` <input type="password" part="textbox textbox-auth-client-secret" value="" placeholder="Client Secret" spellcheck="false" class="oauth-client-secret" style="margin:0 5px"> <select aria-label="oauth client secret location" style="margin-right:5px" class="oauth-send-client-secret-in"> <option value="header" selected="selected"> Authorization Header </option> <option value="request-body"> Request Body </option> </select>` : (0, _litElement.html)`<div style="width:5px"></div>`} ${flowName === 'authorizationCode' || flowName === 'clientCredentials' || flowName === 'implicit' ? (0, _litElement.html)` <button class="m-btn thin-border" part="btn btn-outline" @click="${e => {
|
|
284
|
+
onInvokeOAuthFlow.call(this, apiKeyId, flowName, authorizationUrl, tokenUrl, e);
|
|
285
|
+
}}">GET TOKEN</button>` : ''} </div> <div class="oauth-resp-display red-text small-font-size"></div> ` : ''} </div> `;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function securitySchemeTemplate() {
|
|
289
|
+
const schemes = this.resolvedSpec && this.resolvedSpec.securitySchemes;
|
|
290
|
+
|
|
291
|
+
if (!schemes) {
|
|
292
|
+
return undefined;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const providedApiKeys = schemes.filter(v => v.finalKeyValue);
|
|
296
|
+
return (0, _litElement.html)` <section id="auth" part="section-auth" class="observe-me ${this.renderStyle === 'focused' ? 'section-gap--focused-mode' : 'section-gap'}"> <slot name="authentication"> <div class="section-padding"> <div class="sub-title regular-font">AUTHENTICATION</div> <div class="small-font-size" style="display:flex;align-items:center;min-height:30px"> ${providedApiKeys.length > 0 ? (0, _litElement.html)` <div class="blue-text"> ${providedApiKeys.length} API key applied </div> <div style="flex:1"></div> <button class="m-btn thin-border" part="btn btn-outline" @click="${() => {
|
|
297
|
+
onClearAllApiKeys.call(this);
|
|
298
|
+
}}">CLEAR ALL API KEYS</button>` : (0, _litElement.html)`<div class="red-text">No API key applied</div>`} </div> ${schemes.length > 0 ? (0, _litElement.html)` <table role="presentation" class="m-table" style="width:100%"> ${schemes.map(v => (0, _litElement.html)` <tr> <td style="max-width:500px;overflow-wrap:break-word"> <div style="min-height:24px"> <span style="font-weight:700">${v.typeDisplay}</span> ${v.finalKeyValue ? (0, _litElement.html)` <span class="blue-text"> ${v.finalKeyValue ? 'Key Applied' : ''} </span> <button class="m-btn thin-border small" part="btn btn-outline" @click="${() => {
|
|
299
|
+
v.finalKeyValue = '';
|
|
300
|
+
this.requestUpdate();
|
|
301
|
+
}}">REMOVE</button> ` : ''} </div> ${v.description ? (0, _litElement.html)` <div class="m-markdown"> ${(0, _unsafeHtml.unsafeHTML)((0, _marked.marked)(v.description || ''))} </div>` : ''} </td> <td> ${v.type && (v.type.toLowerCase() === 'apikey' || v.type.toLowerCase() === 'http' && v.scheme && v.scheme.toLowerCase() === 'bearer') ? (0, _litElement.html)` ${v.type.toLowerCase() === 'apikey' ? (0, _litElement.html)`Send <code>${v.name}</code> in <code>${v.in}</code> with the given value:` : (0, _litElement.html)`Send <code>Authorization</code> in <code>header</code> containing the word <code>Bearer</code> followed by a space and a Token String.`} <div style="display:flex"> ${v.in === 'cookie' ? (0, _litElement.html)` <div style="display:block"> <input type="text" value="${getCookieValue(v.apiKeyId)}" disabled="disabled" class="api-key-input" placeholder="IygRVGf54B59e0GAkKmigGfuiVlp/uhFfk2ifA+jMMJzau2F1jPldc09gPTfnMw13BFBxqUZIFDm55DPfwkb0A==" spellcheck="false" style="resize:horizontal;width:100%"> <br> <small> <strong>Cookies</strong> are set and configured by the remote service, therefore it is not possible to configure them from the browser. </small> </div>` : (0, _litElement.html)` <input type="text" value="${v.value}" class="api-key-input" placeholder="api-token" spellcheck="false"> <button class="m-btn thin-border" style="margin-left:5px" part="btn btn-outline" @click="${e => {
|
|
302
|
+
onApiKeyChange.call(this, v.apiKeyId, e);
|
|
303
|
+
}}"> ${v.finalKeyValue ? 'UPDATE' : 'SET'} </button>`} </div>` : ''} ${v.type && v.type.toLowerCase() === 'http' && v.scheme && v.scheme.toLowerCase() === 'basic' ? (0, _litElement.html)` Send the <code>Authorization</code> header containing the type <code>Basic</code> followed by a space and a base64 encoded string of <code>username:password</code>. <div style="display:flex"> <input type="text" value="${v.user}" placeholder="username" spellcheck="false" class="api-key-user" style="width:100px"> <input type="password" value="${v.password}" placeholder="password" spellcheck="false" class="api-key-password" style="width:100px;margin:0 5px"> <button class="m-btn thin-border" @click="${e => {
|
|
304
|
+
onApiKeyChange.call(this, v.apiKeyId, e);
|
|
305
|
+
}}" part="btn btn-outline"> ${v.finalKeyValue ? 'UPDATE' : 'SET'} </button> </div>` : ''} </td> </tr> ${v.type.toLowerCase() === 'oauth2' ? (0, _litElement.html)` <tr> <td colspan="2" style="border:none;padding-left:48px"> ${Object.keys(v.flows).map(f => oAuthFlowTemplate.call(this, f, v, v.flows[f]))} </td> </tr> ` : ''} `)} </table>` : ''} </div> </slot> </section> `;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function getOauthScopeTemplate(scopes) {
|
|
309
|
+
if (!scopes || !scopes.length || !Array.isArray(scopes)) {
|
|
310
|
+
return '';
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
return (0, _litElement.html)` <div> <b>Required scopes:</b> <br> <div style="margin-left:8px"> ${scopes.map(scope => (0, _litElement.html)`<span>${scope}</span> `)} </div> </div>`;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function pathSecurityTemplate(pathSecurity) {
|
|
317
|
+
if (this.resolvedSpec.securitySchemes && pathSecurity) {
|
|
318
|
+
const orSecurityKeys1 = [];
|
|
319
|
+
pathSecurity.forEach(pSecurity => {
|
|
320
|
+
const andSecurityKeys1 = [];
|
|
321
|
+
const andKeyTypes = [];
|
|
322
|
+
Object.keys(pSecurity).forEach(pathSecurityKey => {
|
|
323
|
+
const s = this.resolvedSpec.securitySchemes.find(ss => ss.apiKeyId === pathSecurityKey);
|
|
324
|
+
|
|
325
|
+
if (s) {
|
|
326
|
+
andKeyTypes.push(s.typeDisplay);
|
|
327
|
+
andSecurityKeys1.push({ ...s,
|
|
328
|
+
scopes: pSecurity[pathSecurityKey]
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
orSecurityKeys1.push({
|
|
333
|
+
securityTypes: andKeyTypes.length > 1 ? `${andKeyTypes[0]} + ${andKeyTypes.length - 1} more` : andKeyTypes[0],
|
|
334
|
+
securityDefs: andSecurityKeys1
|
|
335
|
+
});
|
|
336
|
+
});
|
|
337
|
+
return (0, _litElement.html)`<div class="security-info-button" data-content-id="auth" @click="${e => this.scrollToEventTarget(e, false)}"> <div style="position:relative;display:flex;min-width:350px;max-width:700px;justify-content:flex-end"> <svg width="16" height="24" style="cursor:pointer"> <g> <path style="fill:var(--fg3)" d="m13.8,8.5l0,-2.6l0,0c0,-3.2 -2.6,-5.8 -5.8,-5.8s-5.8,2.6 -5.8,5.8l0,0l0,2.6l-2.1,0l0,11.2l16,0l0,-11.2l-2.1,0l-0,0l0,0l0,0l-0,0zm-9.8,-2.6c0,0 0,0 0,0c0,-2.2 1.8,-4 4,-4c2.2,0 4,1.8 4,4c0,0 0,0 0,0l0,2.6l-8.03,0l0,-2.6l0,0l0,0z"/> </g> </svg> ${orSecurityKeys1.map((orSecurityItem1, i) => (0, _litElement.html)` ${i !== 0 ? (0, _litElement.html)`<div style="padding:3px 4px"> OR </div>` : ''} <div class="tooltip" style="cursor:pointer"> <div style="padding:2px 4px;white-space:nowrap;text-overflow:ellipsis;max-width:150px;overflow:hidden"> <span part="anchor anchor-operation-security"> ${orSecurityItem1.securityTypes} </span> </div> <div class="tooltip-text" style="position:absolute;color:var(--fg);top:26px;right:0;border:1px solid var(--border-color);padding:2px 4px;display:block"> ${orSecurityItem1.securityDefs.length > 1 ? (0, _litElement.html)`<div>Requires <b>all</b> of the following </div>` : ''} <div style="padding-left:8px"> ${orSecurityItem1.securityDefs.map((andSecurityItem, j) => (0, _litElement.html)` ${andSecurityItem.type === 'oauth2' ? (0, _litElement.html)` <div> ${orSecurityItem1.securityDefs.length > 1 ? (0, _litElement.html)`<b>${j + 1}.</b> ` : (0, _litElement.html)`Requires`} OAuth token (${andSecurityItem.apiKeyId}) in <b>Authorization header</b> ${getOauthScopeTemplate(andSecurityItem.scopes)} </div>` : andSecurityItem.type === 'http' ? (0, _litElement.html)` <div> ${orSecurityItem1.securityDefs.length > 1 ? (0, _litElement.html)`<b>${j + 1}.</b> ` : (0, _litElement.html)`Requires`} ${andSecurityItem.scheme === 'basic' ? 'Base 64 encoded username:password' : 'Bearer Token'} in <b>Authorization header</b> ${getOauthScopeTemplate(andSecurityItem.scopes)} </div>` : (0, _litElement.html)` <div> ${orSecurityItem1.securityDefs.length > 1 ? (0, _litElement.html)`<b>${j + 1}.</b> ` : (0, _litElement.html)`Requires`} Token in <b>${andSecurityItem.name} ${andSecurityItem.in}</b> ${getOauthScopeTemplate(andSecurityItem.scopes)} </div>`} `)} </div> </div> </div> `)} </div> </div>`;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return '';
|
|
341
|
+
}
|
|
342
|
+
/* eslint-enable indent */
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.default = serverTemplate;
|
|
5
|
+
|
|
6
|
+
var _litElement = require("lit-element");
|
|
7
|
+
|
|
8
|
+
var _marked = require("marked");
|
|
9
|
+
|
|
10
|
+
var _unsafeHtml = require("lit-html/directives/unsafe-html");
|
|
11
|
+
|
|
12
|
+
function onApiServerChange(e, server) {
|
|
13
|
+
if (e && e.target.checked) {
|
|
14
|
+
this.selectedServer = server;
|
|
15
|
+
this.requestUpdate();
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function onApiServerVarChange(e, serverObj) {
|
|
20
|
+
const inputEls = [...e.currentTarget.closest('table').querySelectorAll('input, select')];
|
|
21
|
+
let tempUrl = serverObj.url;
|
|
22
|
+
inputEls.forEach(v => {
|
|
23
|
+
const regex = new RegExp(`{${v.dataset.var}}`, 'g');
|
|
24
|
+
tempUrl = tempUrl.replace(regex, v.value);
|
|
25
|
+
});
|
|
26
|
+
serverObj.computedUrl = tempUrl;
|
|
27
|
+
this.requestUpdate();
|
|
28
|
+
}
|
|
29
|
+
/* eslint-disable indent */
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
function serverVarsTemplate() {
|
|
33
|
+
return this.selectedServer && this.selectedServer.variables ? (0, _litElement.html)` <div class="table-title"> SERVER VARIABLES</div> <table role="presentation" class="m-table"> ${Object.entries(this.selectedServer.variables).map(kv => (0, _litElement.html)` <tr> <td style="vertical-align:middle">${kv[0]}</td> <td> ${kv[1].enum ? (0, _litElement.html)` <select data-var="${kv[0]}" @input="${e => {
|
|
34
|
+
onApiServerVarChange.call(this, e, this.selectedServer);
|
|
35
|
+
}}"> ${Object.entries(kv[1].enum).map(e => kv[1].default === e[1] ? (0, _litElement.html)` <option selected="selected" label="${e[1]}" value="${e[1]}">` : (0, _litElement.html)` <option label="${e[1]}" value="${e[1]}">`)} </select>` : (0, _litElement.html)` <input type="text" part="textbox textbox-server-var" spellcheck="false" data-var="${kv[0]}" value="${kv[1].default}" @input="${e => {
|
|
36
|
+
onApiServerVarChange.call(this, e, this.selectedServer);
|
|
37
|
+
}}">`} </td> </tr> ${kv[1].description ? (0, _litElement.html)`<tr><td colspan="2" style="border:none"><span class="m-markdown-small"> ${(0, _unsafeHtml.unsafeHTML)((0, _marked.marked)(kv[1].description))} </span></td></tr>` : ''} `)} </table> ` : '';
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function serverTemplate() {
|
|
41
|
+
if (!this.resolvedSpec) {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return (0, _litElement.html)` <section id="servers" part="section-servers" style="margin-top:24px;margin-bottom:24px" class="regular-font observe-me section-padding ${this.renderStyle === 'read' ? 'section-gap--read-mode' : this.renderStyle === 'focused' ? 'section-gap--focused-mode' : 'section-gap'}"> <div class="sub-title">API SERVER</div> <div class="mono-font" style="margin:12px 0;font-size:calc(var(--font-size-small) + 1px)"> ${!this.resolvedSpec.servers || !this.resolvedSpec.servers.length ? '' : (0, _litElement.html)` ${this.resolvedSpec.servers.map((server, i) => (0, _litElement.html)` <input type="radio" name="api_server" id="srvr-opt-${i}" value="${server.url}" @change="${e => {
|
|
46
|
+
onApiServerChange.call(this, e, server);
|
|
47
|
+
}}" .checked="${this.selectedServer.url === server.url}" style="margin:4px 0;cursor:pointer"> <label style="cursor:pointer" for="srvr-opt-${i}"> ${server.url} ${server.description ? (0, _litElement.html)`- <span class="regular-font">${server.description} </span>` : ''} </label> <br> `)} `} <div class="table-title primary-text" part="label-selected-server"> SELECTED: ${this.selectedServer && this.selectedServer.computedUrl || 'none'}</div> </div> <slot name="servers"></slot> ${serverVarsTemplate.call(this)} </section>`;
|
|
48
|
+
}
|
|
49
|
+
/* eslint-enable indent */
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
exports.__esModule = true;
|
|
4
|
+
exports.default = void 0;
|
|
5
|
+
|
|
6
|
+
/* eslint-disable no-mixed-operators */
|
|
7
|
+
|
|
8
|
+
/* eslint-disable no-bitwise */
|
|
9
|
+
var _default = {
|
|
10
|
+
color: {
|
|
11
|
+
inputReverseFg: '#fff',
|
|
12
|
+
inputReverseBg: '#333',
|
|
13
|
+
headerBg: '#444',
|
|
14
|
+
|
|
15
|
+
getRgb(hexStr) {
|
|
16
|
+
let hex = (hexStr || '').trim();
|
|
17
|
+
|
|
18
|
+
if (hex.indexOf('#') === 0) {
|
|
19
|
+
hex = hex.slice(1, 7);
|
|
20
|
+
} // convert 3-digit hex to 6-digits.
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if (hex.length === 3 || hex.length === 4) {
|
|
24
|
+
hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
if (hex.length !== 6) {
|
|
28
|
+
// eslint-disable-next-line no-console
|
|
29
|
+
console.error(`Invalid HEX color: '${hexStr}'`);
|
|
30
|
+
return {
|
|
31
|
+
r: 0,
|
|
32
|
+
g: 0,
|
|
33
|
+
b: 0
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
r: parseInt(hex.slice(0, 2), 16),
|
|
39
|
+
g: parseInt(hex.slice(2, 4), 16),
|
|
40
|
+
b: parseInt(hex.slice(4, 6), 16)
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
luminanace(hexColorCode) {
|
|
45
|
+
const rgb = this.getRgb(hexColorCode);
|
|
46
|
+
return rgb.r * 0.299 + rgb.g * 0.587 + rgb.b * 0.114;
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
invert(hexColorCode) {
|
|
50
|
+
// compare with `>=128`, but giving little more preference to white over black
|
|
51
|
+
return this.luminanace(hexColorCode) > 149 ? '#000000' : '#ffffff';
|
|
52
|
+
},
|
|
53
|
+
|
|
54
|
+
// https://stackoverflow.com/a/41491220/5091874
|
|
55
|
+
selectTextColorFromBackground(bcHexColor) {
|
|
56
|
+
const {
|
|
57
|
+
r,
|
|
58
|
+
g,
|
|
59
|
+
b
|
|
60
|
+
} = this.getRgb(bcHexColor);
|
|
61
|
+
const colors = [r / 255, g / 255, b / 255];
|
|
62
|
+
const c = colors.map(col => {
|
|
63
|
+
if (col <= 0.03928) {
|
|
64
|
+
return col / 12.92;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return ((col + 0.055) / 1.055) ** 2.4;
|
|
68
|
+
});
|
|
69
|
+
const L = 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2];
|
|
70
|
+
return L > 0.179 ? '#000000' : '#FFFFFF';
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
opacity(hex, opacity) {
|
|
74
|
+
const rgb = this.getRgb(hex);
|
|
75
|
+
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${opacity})`;
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
brightness(hex, amt) {
|
|
79
|
+
const rgb = this.getRgb(hex);
|
|
80
|
+
rgb.r += amt;
|
|
81
|
+
rgb.g += amt;
|
|
82
|
+
rgb.b += amt;
|
|
83
|
+
|
|
84
|
+
if (rgb.r > 255) {
|
|
85
|
+
rgb.r = 255;
|
|
86
|
+
} else if (rgb.r < 0) {
|
|
87
|
+
rgb.r = 0;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (rgb.g > 255) {
|
|
91
|
+
rgb.g = 255;
|
|
92
|
+
} else if (rgb.g < 0) {
|
|
93
|
+
rgb.g = 0;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (rgb.b > 255) {
|
|
97
|
+
rgb.b = 255;
|
|
98
|
+
} else if (rgb.b < 0) {
|
|
99
|
+
rgb.b = 0;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return `#${rgb.r.toString(16).padStart(2, '0')}${rgb.g.toString(16).padStart(2, '0')}${rgb.b.toString(16).padStart(2, '0')}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
},
|
|
106
|
+
|
|
107
|
+
isValidHexColor(colorCode) {
|
|
108
|
+
return /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}|[A-Fa-f0-9]{8}|[A-Fa-f0-9]{4})$/i.test(colorCode);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
};
|
|
112
|
+
exports.default = _default;
|