openapi-explorer
Version:
OpenAPI Explorer - API viewer with dynamically generated components, documentation, and interaction console
172 lines (169 loc) • 7.15 kB
JavaScript
import { marked } from 'marked';
import { getI18nText } from '../languages/index.js';
/* For Delayed Event Handler Execution */
export function debounce(fn, delay) {
let timeoutID = null;
return (...args) => {
clearTimeout(timeoutID);
const that = this;
timeoutID = setTimeout(() => {
fn.apply(that, args);
}, delay);
};
}
export const invalidCharsRegEx = new RegExp(/[\s#:?&={}-]+/, 'g'); // used for generating valid html element ids by replacing the invalid chars with hyphen (-)
export function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
export function copyToClipboard(copyData, eventTarget) {
// In lots of places we have more than a couple of spaces for <pre> display purposes, we remove those extra spaces here.
let data = copyData === null || copyData === void 0 ? void 0 : copyData.trim().replace(/\s{8}/g, ' ');
try {
// If the parsed type is a number, then leave it alone
if (typeof JSON.parse(data) === 'object') {
// Convert to 2 spaces in all JSON text
data = JSON.stringify(JSON.parse(data), null, 2).trim();
}
} catch (error) {
// Ignore non JSON text;
}
const textArea = document.createElement('textarea');
textArea.value = data;
textArea.style.position = 'fixed'; // avoid scrolling to bottom
document.body.appendChild(textArea);
textArea.focus();
textArea.select();
try {
document.execCommand('copy');
const btnEl = eventTarget === null || eventTarget === void 0 ? void 0 : eventTarget.target;
if (btnEl) {
btnEl.innerText = getI18nText('operations.copied');
setTimeout(() => {
btnEl.innerText = getI18nText('operations.copy');
}, 5000);
}
} catch (err) {
console.error('Unable to copy', err); // eslint-disable-line no-console
}
document.body.removeChild(textArea);
}
export function getBaseUrlFromUrl(url) {
const pathArray = url.split('/');
return `${pathArray[0]}//${pathArray[2]}`;
}
export function componentIsInSearch(searchVal, component) {
return !searchVal || component.name.toLowerCase().includes(searchVal.toLowerCase());
}
export function pathIsInSearch(searchVal, path) {
if (!searchVal) {
return true;
}
const stringToSearch = `${path.method} ${path.path} ${path.summary || ''} ${path.description || ''} ${path.operationId || ''}`;
return stringToSearch.includes(searchVal) || stringToSearch.toLowerCase().includes(searchVal) || stringToSearch.normalize('NFD').replace(/[\u0300-\u036f]/g, '').includes(searchVal) || stringToSearch.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '').includes(searchVal);
}
function schemaKeys(schemaProps, result = new Set()) {
if (!schemaProps) {
return result;
}
Object.keys(schemaProps).forEach(key => {
result.add(key);
if (schemaProps[key].properties) {
schemaKeys(schemaProps[key].properties, result);
} else if (schemaProps[key].items && schemaProps[key].items.properties) {
schemaKeys(schemaProps[key].items.properties, result);
}
});
return result;
}
export function advancedSearch(searchVal, allSpecTags, searchOptions = []) {
if (!searchVal.trim() || searchOptions.length === 0) {
return undefined;
}
const pathsMatched = [];
allSpecTags.forEach(tag => {
tag.paths.forEach(path => {
let stringToSearch = '';
if (searchOptions.includes('search-api-path')) {
stringToSearch = path.path;
}
if (searchOptions.includes('search-api-descr')) {
stringToSearch = `${stringToSearch} ${path.summary || path.description || ''}`;
}
if (searchOptions.includes('search-api-params')) {
stringToSearch = `${stringToSearch} ${path.parameters && path.parameters.map(v => v.name).join(' ') || ''}`;
}
if (searchOptions.includes('search-api-request-body') && path.requestBody) {
let schemaKeySet = new Set();
for (const contentType in path.requestBody && path.requestBody.content) {
if (path.requestBody.content[contentType].schema && path.requestBody.content[contentType].schema.properties) {
schemaKeySet = schemaKeys(path.requestBody.content[contentType].schema.properties);
}
stringToSearch = `${stringToSearch} ${[...schemaKeySet].join(' ')}`;
}
}
if (searchOptions.includes('search-api-resp-descr')) {
stringToSearch = `${stringToSearch} ${Object.values(path.responses).map(v => v.description || '').join(' ')}`;
}
if (stringToSearch.toLowerCase().includes(searchVal.trim().toLowerCase())) {
pathsMatched.push({
elementId: path.elementId,
method: path.method,
path: path.path,
summary: path.summary || path.description || '',
deprecated: path.deprecated
});
}
});
});
return pathsMatched;
}
export function getCurrentElement() {
const currentQuery = (window.location.hash || '').split('?')[1];
const query = new URLSearchParams(currentQuery);
return decodeURIComponent(query.get('route') || '');
}
export function replaceState(rawElementId) {
const elementId = rawElementId && rawElementId.replace(/^#/, '') || '';
const currentNavigationHashPart = (window.location.hash || '').split('?')[0].replace(/^#/, '');
const currentQuery = (window.location.hash || '').split('?')[1];
const query = new URLSearchParams(currentQuery);
query.delete('route');
const newQuery = query.toString().length > 1 ? `${query.toString()}&route=${elementId}` : `route=${elementId}`;
const fragment = `#${currentNavigationHashPart}?${newQuery}`;
const currentHref = window.location.href;
const url = new URL(fragment, currentHref);
if (url.href !== currentHref) {
window.history.pushState(null, null, url.href);
}
}
export function toMarkdown(markdownStringRaw) {
const sanitizedMarkdownString = (markdownStringRaw || ''
// Convert scripts tags to correct markdown format
).replace(/[<]script[^>]*>/gi, '<div>```').replace(/[<][/]script/gi, '```</div')
// Remove unnecessary attributes from img tag
.replace(/onerror=/ig, 'attribute');
const markdownResult = marked(sanitizedMarkdownString || '');
return markdownResult;
}
export function getSanitizedUrl(urlString) {
if (!urlString) {
return '';
}
try {
// eslint-disable-next-line no-new
const url = new URL(urlString);
return url.protocol === 'http' || url.protocol === 'https' ? url : '';
} catch (error) {
return '';
}
}
export function getSanitizedEmail(emailRaw) {
if (!emailRaw) {
return '';
}
// eslint-disable-next-line max-len, no-control-regex
if (emailRaw.match(/(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/)) {
return emailRaw;
}
return '';
}