UNPKG

vitepress-openapi

Version:

Generate VitePress API Documentation from OpenAPI Specification.

264 lines (238 loc) 7.31 kB
import type { OpenAPIV3 } from '@scalar/openapi-types' import type { OpenAPIDocument, ParsedOpenAPI, ParsedPaths } from '../../types' import { httpVerbs } from '../../index' export interface OpenApiSpecInstance { spec: OpenAPIDocument originalSpec: OpenAPIDocument | null getSpec: () => OpenAPIDocument setSpec: (spec: OpenAPIDocument) => void getOriginalSpec: () => OpenAPIDocument | null setOriginalSpec: (spec: OpenAPIDocument | null) => void getOperation: (operationId: string) => any | null getOperationPath: (operationId: string) => string | null getOperationMethod: (operationId: string) => string | null getOperationParameters: (operationId: string) => any[] getPaths: () => ParsedPaths getPathsByVerbs: () => any[] getInfo: () => any getExternalDocs: () => any getServers: () => any[] getOperationServers: (operationId: string) => OpenAPIV3.ServerObject[] getOperationsTags: () => string[] getPathsByTags: (tags: string | string[]) => OpenAPIV3.PathsObject getPathsWithoutTags: () => OpenAPIV3.PathsObject getTags: () => { name: string | null, description: string | null }[] getFilteredTags: () => { name: string | null, description: string | null }[] } export function createOpenApiSpec(options: { spec?: ParsedOpenAPI | OpenAPIDocument | null originalSpec?: OpenAPIDocument | null } = {}): OpenApiSpecInstance { let innerSpec: OpenAPIDocument | null = null let innerOriginalSpec: OpenAPIDocument | null = options.originalSpec ?? null function setSpec(spec: OpenAPIDocument) { innerSpec = spec } function getSpec(): OpenAPIDocument { if (!innerSpec) { setSpec((options.spec ?? {}) as OpenAPIDocument) } if (!innerSpec) { throw new Error('OpenAPI spec is not defined') } return innerSpec } function getOriginalSpec(): OpenAPIDocument | null { return innerOriginalSpec } function setOriginalSpec(spec: OpenAPIDocument | null) { innerOriginalSpec = spec } function findOperation(paths: OpenAPIV3.PathsObject, operationId: string) { for (const path of Object.values(paths)) { for (const verb of httpVerbs) { if (path && path[verb]?.operationId === operationId) { return path[verb] } } } return null } function getOperation(operationId: string) { const paths = getSpec().paths as OpenAPIV3.PathsObject if (!paths) { return null } return findOperation(paths, operationId) } function getOperationPath(operationId: string) { const paths = getSpec().paths as OpenAPIV3.PathsObject if (!paths) { return null } for (const [path, methods] of Object.entries(paths)) { for (const verb of httpVerbs) { if (methods && methods[verb]?.operationId === operationId) { return path } } } return null } function getOperationMethod(operationId: string) { const paths = getSpec().paths as OpenAPIV3.PathsObject if (!paths) { return null } for (const path of Object.values(paths)) { for (const verb of httpVerbs) { if (path && path[verb]?.operationId === operationId) { return verb } } } return null } function getOperationParameters(operationId: string) { const operation = getOperation(operationId) if (!operation) { return [] } return operation.parameters || [] } function getPaths(): ParsedPaths { return (getSpec().paths ?? {}) as ParsedPaths } function getPathsByVerbs() { const paths = getPaths() return Object.keys(paths) .flatMap((path) => { return httpVerbs .filter((verb: string) => paths && paths[path] && paths[path][verb]) .map((verb: string) => { if (!paths || !paths[path] || !paths[path][verb]) { return null } const { operationId, summary, tags } = paths[path][verb] return { path, verb, operationId, summary, tags: tags ?? [], } }) }) } function getInfo() { return getSpec().info ?? {} } function getExternalDocs() { return getSpec().externalDocs ?? {} } function getServers() { return getSpec().servers ?? [] } function getOperationServers(operationId: string): OpenAPIV3.ServerObject[] { const operation = findOperation(getPaths(), operationId) if (!operation) { return [] } const operationPath = getOperationPath(operationId) const paths = getSpec().paths as OpenAPIV3.PathsObject const pathServers = paths[(operationPath ?? '')]?.servers if (operation?.servers !== undefined) { return operation.servers as OpenAPIV3.ServerObject[] } if (pathServers !== undefined) { return pathServers as OpenAPIV3.ServerObject[] } return getSpec().servers ?? [] } function getOperationsTags(): string[] { if (!getSpec().paths) { return [] } const paths = getSpec().paths as OpenAPIV3.PathsObject return Object.values(paths).reduce((tags: string[], path) => { for (const verb of httpVerbs) { if (path && path[verb]?.tags) { path[verb].tags.forEach((tag: string) => { if (!tags.includes(tag)) { tags.push(tag) } }) } } return tags }, []) } function filterPaths(predicate: (operation: any) => boolean) { const paths = getPaths() ?? {} const output: OpenAPIV3.PathsObject = {} for (const [path, methods] of Object.entries(paths)) { if (!methods) { continue } for (const verb of httpVerbs) { const operation = methods[verb] if (operation && predicate(operation)) { output[path] = output[path] || {} output[path][verb] = operation } } } return output } function getPathsByTags(tags: string | string[]) { const tagList = typeof tags === 'string' ? [tags] : tags return filterPaths(operation => operation?.tags?.some((tag: string) => tagList.includes(tag))) } function getPathsWithoutTags() { return filterPaths(operation => !operation?.tags || operation.tags.length === 0) } function getTags() { return (getSpec().tags ?? []) .map(tag => ({ name: tag.name ?? null, description: tag.description ?? null, })) } function getFilteredTags() { const operationsTags = getOperationsTags() const tags = getTags() .filter(tag => operationsTags.includes(tag.name ?? '')) return tags .concat([ ...operationsTags .filter(tag => !tags.map(tag => tag.name).includes(tag)) .map(tag => ({ name: tag, description: null, })), ]) } return { spec: getSpec(), originalSpec: getOriginalSpec(), setSpec, getSpec, getOriginalSpec, setOriginalSpec, getOperation, getOperationPath, getOperationMethod, getOperationParameters, getPaths, getPathsByVerbs, getInfo, getExternalDocs, getServers, getOperationServers, getOperationsTags, getPathsByTags, getPathsWithoutTags, getTags, getFilteredTags, } }