| 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, |
| } |
| } |
| } |
|
|