UNPKG

rapidoc

Version:

RapiDoc - Open API spec viewer with built in console

383 lines (348 loc) 14.7 kB
import { css, LitElement } from 'lit'; import { marked } from 'marked'; import Prism from 'prismjs'; import 'prismjs/components/prism-css'; import 'prismjs/components/prism-yaml'; import 'prismjs/components/prism-go'; import 'prismjs/components/prism-java'; import 'prismjs/components/prism-json'; import 'prismjs/components/prism-bash'; import 'prismjs/components/prism-python'; import 'prismjs/components/prism-http'; import 'prismjs/components/prism-csharp'; // Styles import FontStyles from '~/styles/font-styles'; import InputStyles from '~/styles/input-styles'; import FlexStyles from '~/styles/flex-styles'; import TableStyles from '~/styles/table-styles'; import PrismStyles from '~/styles/prism-styles'; import TabStyles from '~/styles/tab-styles'; import NavStyles from '~/styles/nav-styles'; import InfoStyles from '~/styles/info-styles'; import EndpointStyles from '~/styles/endpoint-styles'; import { rapidocApiKey } from '~/utils/common-utils'; import ProcessSpec from '~/utils/spec-parser'; import mainBodyTemplate from '~/templates/main-body-template'; import { applyApiKey, onClearAllApiKeys } from '~/templates/security-scheme-template'; import { setApiServer } from '~/templates/server-template'; export default class RapiDocMini extends LitElement { constructor() { super(); this.isMini = true; this.updateRoute = 'false'; this.renderStyle = 'view'; this.showHeader = 'false'; this.allowAdvancedSearch = 'false'; } static get properties() { return { // Spec specUrl: { type: String, attribute: 'spec-url' }, sortEndpointsBy: { type: String, attribute: 'sort-endpoints-by' }, // UI Layouts layout: { type: String }, pathsExpanded: { type: String, attribute: 'paths-expanded' }, defaultSchemaTab: { type: String, attribute: 'default-schema-tab' }, responseAreaHeight: { type: String, attribute: 'response-area-height' }, showSummaryWhenCollapsed: { type: String, attribute: 'show-summary-when-collapsed' }, fillRequestFieldsWithExample: { type: String, attribute: 'fill-request-fields-with-example' }, persistAuth: { type: String, attribute: 'persist-auth' }, // Schema Styles schemaStyle: { type: String, attribute: 'schema-style' }, schemaExpandLevel: { type: Number, attribute: 'schema-expand-level' }, schemaDescriptionExpanded: { type: String, attribute: 'schema-description-expanded' }, // API Server apiKeyName: { type: String, attribute: 'api-key-name' }, apiKeyLocation: { type: String, attribute: 'api-key-location' }, apiKeyValue: { type: String, attribute: 'api-key-value' }, defaultApiServerUrl: { type: String, attribute: 'default-api-server' }, serverUrl: { type: String, attribute: 'server-url' }, oauthReceiver: { type: String, attribute: 'oauth-receiver' }, allowTry: { type: String, attribute: 'allow-try' }, showCurlBeforeTry: { type: String, attribute: 'show-curl-before-try' }, // Main Colors and Font theme: { type: String }, bgColor: { type: String, attribute: 'bg-color' }, textColor: { type: String, attribute: 'text-color' }, primaryColor: { type: String, attribute: 'primary-color' }, fontSize: { type: String, attribute: 'font-size' }, regularFont: { type: String, attribute: 'regular-font' }, monoFont: { type: String, attribute: 'mono-font' }, loadFonts: { type: String, attribute: 'load-fonts' }, // Fetch Options fetchCredentials: { type: String, attribute: 'fetch-credentials' }, // Filters matchPaths: { type: String, attribute: 'match-paths' }, matchType: { type: String, attribute: 'match-type' }, removeEndpointsWithBadgeLabelAs: { type: String, attribute: 'remove-endpoints-with-badge-label-as' }, // Internal Properties loading: { type: Boolean }, // indicates spec is being loaded }; } static get styles() { return [ FontStyles, InputStyles, FlexStyles, TableStyles, EndpointStyles, PrismStyles, TabStyles, NavStyles, InfoStyles, css` :host { all: initial; display:flex; flex-direction: column; min-width:360px; width:100%; height:100%; margin:0; padding:0; overflow: hidden; letter-spacing:normal; color:var(--fg); background-color:var(--bg); font-family:var(--font-regular); container-type: inline-size; } @container (min-width: 768px) { .only-large-screen { display:block; } .only-large-screen-flex { display:flex; } }`, ]; } // Startup connectedCallback() { super.connectedCallback(); if (this.loadFonts !== 'false') { const fontDescriptor = { family: 'Open Sans', style: 'normal', weight: '300', unicodeRange: 'U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD', }; const fontWeight300 = new FontFace( 'Open Sans', "url(https://fonts.gstatic.com/s/opensans/v18/mem5YaGs126MiZpBA-UN_r8OUuhpKKSTjw.woff2) format('woff2')", fontDescriptor, ); fontDescriptor.weight = '600'; const fontWeight600 = new FontFace( 'Open Sans', "url(https://fonts.gstatic.com/s/opensans/v18/mem5YaGs126MiZpBA-UNirkOUuhpKKSTjw.woff2) format('woff2')", fontDescriptor, ); fontWeight300.load().then((font) => { document.fonts.add(font); }); fontWeight600.load().then((font) => { document.fonts.add(font); }); } if (!this.showSummaryWhenCollapsed || !'true, false,'.includes(`${this.showSummaryWhenCollapsed},`)) { this.showSummaryWhenCollapsed = 'true'; } if (!this.layout || !'row, column,'.includes(`${this.layout},`)) { this.layout = 'row'; } if (!this.schemaStyle || !'tree, table,'.includes(`${this.schemaStyle},`)) { this.schemaStyle = 'tree'; } if (!this.theme || !'light, dark,'.includes(`${this.theme},`)) { this.theme = (window.matchMedia && window.matchMedia('(prefers-color-scheme: light)').matches) ? 'light' : 'dark'; } if (!this.defaultSchemaTab || !'example, schema, model,'.includes(`${this.defaultSchemaTab},`)) { this.defaultSchemaTab = 'example'; } else if (this.defaultSchemaTab === 'model') { this.defaultSchemaTab = 'schema'; } this.pathsExpanded = this.pathsExpanded === 'true'; if (!this.schemaExpandLevel || this.schemaExpandLevel < 1) { this.schemaExpandLevel = 99999; } if (!this.schemaDescriptionExpanded || !'true, false,'.includes(`${this.schemaDescriptionExpanded},`)) { this.schemaDescriptionExpanded = 'false'; } if (!this.fillRequestFieldsWithExample || !'true, false,'.includes(`${this.fillRequestFieldsWithExample},`)) { this.fillRequestFieldsWithExample = 'true'; } if (!this.persistAuth || !'true, false,'.includes(`${this.persistAuth},`)) { this.persistAuth = 'false'; } if (!this.responseAreaHeight) { this.responseAreaHeight = '300px'; } if (!this.allowTry || !'true, false,'.includes(`${this.allowTry},`)) { this.allowTry = 'true'; } if (!this.apiKeyValue) { this.apiKeyValue = '-'; } if (!this.apiKeyLocation) { this.apiKeyLocation = 'header'; } if (!this.apiKeyName) { this.apiKeyName = ''; } if (!this.oauthReceiver) { this.oauthReceiver = 'oauth-receiver.html'; } if (!this.sortTags || !'true, false,'.includes(`${this.sortTags},`)) { this.sortTags = 'false'; } if (!this.sortEndpointsBy || !'method, path, summary,'.includes(`${this.sortEndpointsBy},`)) { this.sortEndpointsBy = 'path'; } if (!this.fontSize || !'default, large, largest,'.includes(`${this.fontSize},`)) { this.fontSize = 'default'; } if (!this.matchType || !'includes regex'.includes(this.matchType)) { this.matchType = 'includes'; } if (!this.matchPaths) { this.matchPaths = ''; } if (!this.removeEndpointsWithBadgeLabelAs) { this.removeEndpointsWithBadgeLabelAs = ''; } if (!this.allowSchemaDescriptionExpandToggle || !'true, false,'.includes(`${this.allowSchemaDescriptionExpandToggle},`)) { this.allowSchemaDescriptionExpandToggle = 'true'; } if (!this.fetchCredentials || !'omit, same-origin, include,'.includes(`${this.fetchCredentials},`)) { this.fetchCredentials = ''; } marked.setOptions({ highlight: (code, lang) => { if (Prism.languages[lang]) { return Prism.highlight(code, Prism.languages[lang], lang); } return code; }, }); } render() { return mainBodyTemplate.call(this, true, this.pathsExpanded); } attributeChangedCallback(name, oldVal, newVal) { if (name === 'spec-url') { if (oldVal !== newVal) { // put it at the end of event-loop to load all the attributes window.setTimeout(async () => { await this.loadSpec(newVal); }, 0); } } if (name === 'match-paths' || name === 'match-type' || name === 'remove-endpoints-with-badge-label-as') { if (oldVal !== newVal) { window.setTimeout(async () => { await this.loadSpec(this.specUrl); }, 0); } } if (name === 'api-key-name' || name === 'api-key-location' || name === 'api-key-value') { let updateSelectedApiKey = false; let apiKeyName = ''; let apiKeyLocation = ''; let apiKeyValue = ''; if (name === 'api-key-name') { if (this.getAttribute('api-key-location') && this.getAttribute('api-key-value')) { apiKeyName = newVal; apiKeyLocation = this.getAttribute('api-key-location'); apiKeyValue = this.getAttribute('api-key-value'); updateSelectedApiKey = true; } } else if (name === 'api-key-location') { if (this.getAttribute('api-key-name') && this.getAttribute('api-key-value')) { apiKeyLocation = newVal; apiKeyName = this.getAttribute('api-key-name'); apiKeyValue = this.getAttribute('api-key-value'); updateSelectedApiKey = true; } } else if (name === 'api-key-value') { if (this.getAttribute('api-key-name') && this.getAttribute('api-key-location')) { apiKeyValue = newVal; apiKeyLocation = this.getAttribute('api-key-location'); apiKeyName = this.getAttribute('api-key-name'); updateSelectedApiKey = true; } } if (updateSelectedApiKey) { if (this.resolvedSpec) { const rapiDocApiKey = this.resolvedSpec.securitySchemes.find((v) => v.securitySchemeId === rapidocApiKey); if (!rapiDocApiKey) { this.resolvedSpec.securitySchemes.push({ apiKeyId: rapidocApiKey, description: 'api-key provided in rapidoc element attributes', type: 'apiKey', name: apiKeyName, in: apiKeyLocation, value: apiKeyValue, finalKeyValue: apiKeyValue, }); } else { rapiDocApiKey.name = apiKeyName; rapiDocApiKey.in = apiKeyLocation; rapiDocApiKey.value = apiKeyValue; rapiDocApiKey.finalKeyValue = apiKeyValue; } this.requestUpdate(); } } } super.attributeChangedCallback(name, oldVal, newVal); } onSpecUrlChange() { this.setAttribute('spec-url', this.shadowRoot.getElementById('spec-url').value); } // Public Method async loadSpec(specUrl) { if (!specUrl) { return; } try { this.resolvedSpec = { specLoadError: false, isSpecLoading: true, tags: [], }; this.loading = true; this.loadFailed = false; this.requestUpdate(); const spec = await ProcessSpec.call( this, specUrl, this.generateMissingTags === 'true', this.sortTags === 'true', this.sortSchemas === 'true', this.getAttribute('sort-endpoints-by'), this.getAttribute('api-key-name'), this.getAttribute('api-key-location'), this.getAttribute('api-key-value'), this.getAttribute('server-url'), this.matchPaths, this.matchType, this.removeEndpointsWithBadgeLabelAs, ); this.loading = false; this.afterSpecParsedAndValidated(spec); } catch (err) { this.loading = false; this.loadFailed = true; this.resolvedSpec = null; console.error(`RapiDoc: Unable to resolve the API spec.. ${err.message}`); // eslint-disable-line no-console } } // Public Method - to update security-scheme of type http setHttpUserNameAndPassword(securitySchemeId, username, password) { return applyApiKey.call(this, securitySchemeId, username, password); } // Public Method - to update security-scheme of type apiKey or OAuth setApiKey(securitySchemeId, apiKeyValue) { return applyApiKey.call(this, securitySchemeId, '', '', apiKeyValue); } // Public Method removeAllSecurityKeys() { return onClearAllApiKeys.call(this); } // Public Method setApiServer(apiServerUrl) { // return apiServerUrl; return setApiServer.call(this, apiServerUrl); } async afterSpecParsedAndValidated(spec) { this.resolvedSpec = spec; this.selectedServer = undefined; if (this.defaultApiServerUrl) { if (this.defaultApiServerUrl === this.serverUrl) { this.selectedServer = { url: this.serverUrl, computedUrl: this.serverUrl, }; } else if (this.resolvedSpec.servers) { this.selectedServer = this.resolvedSpec.servers.find((v) => (v.url === this.defaultApiServerUrl)); } } if (!this.selectedServer) { if (this.resolvedSpec.servers) { this.selectedServer = this.resolvedSpec.servers[0]; // eslint-disable-line prefer-destructuring } } this.requestUpdate(); // eslint-disable-next-line no-await-in-loop while (!await this.updateComplete); const specLoadedEvent = new CustomEvent('spec-loaded', { detail: spec }); this.dispatchEvent(specLoadedEvent); } // Called by anchor tags created using markdown handleHref(e) { if (e.target.tagName.toLowerCase() === 'a') { if (e.target.getAttribute('href').startsWith('#')) { const gotoEl = this.shadowRoot.getElementById(e.target.getAttribute('href').replace('#', '')); if (gotoEl) { gotoEl.scrollIntoView({ behavior: 'auto', block: 'start' }); } } } } } customElements.define('rapi-doc-mini', RapiDocMini);