| | import type { Context, Page } from '@/types' |
| | import type { PageTransformer } from './types' |
| | import type { Operation } from '@/rest/components/types' |
| | import { renderContent } from '@/content-render/index' |
| | import matter from '@gr2m/gray-matter' |
| | import { readFileSync } from 'fs' |
| | import { join, dirname } from 'path' |
| | import { fileURLToPath } from 'url' |
| | import { fastTextOnly } from '@/content-render/unified/text-only' |
| |
|
| | const __filename = fileURLToPath(import.meta.url) |
| | const __dirname = dirname(__filename) |
| |
|
| | |
| | |
| | |
| | |
| | export class RestTransformer implements PageTransformer { |
| | canTransform(page: Page): boolean { |
| | |
| | |
| | return page.autogenerated === 'rest' && !page.relativePath.endsWith('index.md') |
| | } |
| |
|
| | async transform( |
| | page: Page, |
| | pathname: string, |
| | context: Context, |
| | apiVersion?: string, |
| | ): Promise<string> { |
| | |
| | const { default: getRest } = await import('@/rest/lib/index') |
| |
|
| | |
| | const currentVersion = context.currentVersion! |
| |
|
| | |
| | const effectiveApiVersion = |
| | apiVersion || |
| | (context.currentVersionObj?.apiVersions?.length |
| | ? context.currentVersionObj.latestApiVersion |
| | : undefined) |
| |
|
| | |
| | |
| | const pathParts = pathname.split('/').filter(Boolean) |
| | const restIndex = pathParts.indexOf('rest') |
| |
|
| | if (restIndex === -1 || restIndex >= pathParts.length - 1) { |
| | throw new Error(`Invalid REST path: ${pathname}`) |
| | } |
| |
|
| | const category = pathParts[restIndex + 1] |
| | const subcategory = pathParts[restIndex + 2] |
| |
|
| | |
| | const restData = await getRest(currentVersion, effectiveApiVersion) |
| |
|
| | let operations: Operation[] = [] |
| |
|
| | if (subcategory && restData[category]?.[subcategory]) { |
| | operations = restData[category][subcategory] |
| | } else if (category && restData[category]) { |
| | |
| | const categoryData = restData[category] |
| | |
| | operations = Object.values(categoryData).flat() |
| | } |
| |
|
| | |
| | let manualContent = '' |
| | if (page.markdown) { |
| | const markerIndex = page.markdown.indexOf( |
| | '<!-- Content after this section is automatically generated -->', |
| | ) |
| | if (markerIndex > 0) { |
| | const { content } = matter(page.markdown) |
| | const manualContentMarkerIndex = content.indexOf( |
| | '<!-- Content after this section is automatically generated -->', |
| | ) |
| | if (manualContentMarkerIndex > 0) { |
| | const rawManualContent = content.substring(0, manualContentMarkerIndex).trim() |
| | if (rawManualContent) { |
| | manualContent = await renderContent(rawManualContent, { |
| | ...context, |
| | markdownRequested: true, |
| | }) |
| | } |
| | } |
| | } |
| | } |
| |
|
| | |
| | const templateData = await this.prepareTemplateData( |
| | page, |
| | operations, |
| | context, |
| | manualContent, |
| | effectiveApiVersion, |
| | ) |
| |
|
| | |
| | const templatePath = join(__dirname, '../templates/rest-page.template.md') |
| | const templateContent = readFileSync(templatePath, 'utf8') |
| |
|
| | |
| | const rendered = await renderContent(templateContent, { |
| | ...context, |
| | ...templateData, |
| | markdownRequested: true, |
| | }) |
| |
|
| | return rendered |
| | } |
| |
|
| | |
| | |
| | |
| | private async prepareTemplateData( |
| | page: Page, |
| | operations: Operation[], |
| | context: Context, |
| | manualContent: string, |
| | apiVersion?: string, |
| | ): Promise<Record<string, any>> { |
| | |
| | const intro = page.intro ? await page.renderProp('intro', context, { textOnly: true }) : '' |
| |
|
| | |
| | const preparedOperations = await Promise.all( |
| | operations.map(async (operation) => await this.prepareOperation(operation)), |
| | ) |
| |
|
| | return { |
| | page: { |
| | title: page.title, |
| | intro, |
| | }, |
| | manualContent, |
| | restOperations: preparedOperations, |
| | apiVersion, |
| | } |
| | } |
| |
|
| | |
| | |
| | |
| | private async prepareOperation(operation: Operation): Promise<Record<string, any>> { |
| | |
| | const description = operation.descriptionHTML ? fastTextOnly(operation.descriptionHTML) : '' |
| |
|
| | |
| | const needsContentTypeHeader = operation.subcategory === 'inference' |
| | const omitHeaders = |
| | operation.subcategory === 'management-console' || operation.subcategory === 'manage-ghes' |
| | const showHeaders = !omitHeaders |
| |
|
| | |
| | const hasParameters = |
| | (operation.parameters?.length || 0) > 0 || (operation.bodyParameters?.length || 0) > 0 |
| |
|
| | |
| | const statusCodes = operation.statusCodes?.map((statusCode) => ({ |
| | ...statusCode, |
| | description: statusCode.description ? fastTextOnly(statusCode.description) : undefined, |
| | })) |
| |
|
| | |
| | const codeExamples = |
| | operation.codeExamples?.map((example) => { |
| | let url = `${operation.serverUrl}${operation.requestPath}` |
| |
|
| | |
| | if (example.request?.parameters && Object.keys(example.request.parameters).length > 0) { |
| | for (const [key, value] of Object.entries(example.request.parameters)) { |
| | url = url.replace(`{${key}}`, String(value)) |
| | } |
| | } |
| |
|
| | return { |
| | request: { |
| | description: example.request?.description |
| | ? fastTextOnly(example.request.description) |
| | : '', |
| | url, |
| | acceptHeader: example.request?.acceptHeader, |
| | bodyParameters: example.request?.bodyParameters |
| | ? JSON.stringify(example.request.bodyParameters, null, 2) |
| | : null, |
| | }, |
| | response: { |
| | statusCode: example.response?.statusCode, |
| | schema: (example.response as any)?.schema |
| | ? JSON.stringify((example.response as any).schema, null, 2) |
| | : null, |
| | }, |
| | } |
| | }) || [] |
| |
|
| | return { |
| | ...operation, |
| | description, |
| | hasParameters, |
| | showHeaders, |
| | needsContentTypeHeader, |
| | statusCodes, |
| | codeExamples, |
| | } |
| | } |
| | } |
| |
|