Files
supabase/apps/docs/spec/sections/generateMgmtApiSections.cts
Hieu 4ff4cdc62d feat: expose mgmt api v2 spec on official document (#46605)
## I have read the
[CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md)
file.

YES

## What kind of change does this PR introduce?

Include the v2 mgmt API spec in the official docs. This PR merges the v1
and v2 specs when generating the reference, with v2 taking precedence on
conflicts.

Fixes API-1215

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Docs now include API v2 reference: log-drain management
(list/create/update/delete) and project transfer (preview/transfer).

* **Documentation**
* Generated API reference now merges v1 and v2 specs so both appear in
the docs and section listings.
* New v2 operations added to Analytics and Projects documentation
sections.

* **Chores**
* Docs generation pipeline updated to accept and process multiple API
spec inputs.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2026-06-04 14:53:26 +07:00

96 lines
2.7 KiB
TypeScript

import fs from 'fs'
import path from 'path'
function slugToTitle(slug) {
if (!slug) return ''
const prefixRegex = /^v\d+/
const title = slug.replace(prefixRegex, '').replace(/-/g, ' ').trimStart()
return title.charAt(0).toUpperCase() + title.slice(1)
}
function isValidSlug(slug) {
const slugRegex = /^[a-z0-9]+(?:-[a-z0-9]+)*$/
return slugRegex.test(slug)
}
function readJson(filePath: string) {
return JSON.parse(fs.readFileSync(filePath, 'utf8'))
}
function extractSectionsFromOpenApi(filePaths: string[], outputPath: string) {
try {
// Merge paths from all specs, later specs override earlier ones on conflict
const mergedPaths = Object.assign({}, ...filePaths.map((p) => readJson(p).paths ?? {}))
const categories: string[] = []
const sections: Array<{
type: string
title: string
id?: string
slug?: string
items: Array<{
type: string
title: string
id: string
slug: string
}>
}> = []
for (const route in mergedPaths) {
const methods = mergedPaths[route]
for (const method in methods) {
if (methods[method]['x-internal']) {
continue
}
const tag = methods[method].tags?.[0]
const operationId = methods[method].operationId
if (!tag || !isValidSlug(operationId)) continue
if (!categories.includes(tag)) {
categories.push(tag)
sections.push({
type: 'category',
title: tag,
items: [],
})
}
const sectionCate = sections.find((i) => i.title === tag)
sectionCate?.items.push({
id: operationId,
title: slugToTitle(operationId),
slug: operationId,
type: 'operation',
})
}
}
sections.sort((a, b) => a.title.localeCompare(b.title))
sections.forEach((i) => i.items.sort((a, b) => a.title.localeCompare(b.title)))
sections.unshift({
title: 'Introduction',
id: 'introduction',
slug: 'introduction',
type: 'markdown',
items: [],
})
fs.writeFileSync(outputPath, JSON.stringify(sections, null, 2), 'utf8')
console.log(`Sections successfully generated!!!`)
} catch (error) {
console.error('Error:', error)
}
}
const args = process.argv.slice(2)
if (args.length < 2) {
console.error(
'Please provide at least one openapi file path and an output file path as arguments.'
)
process.exit(1)
}
// Last arg is output, everything before is input files
const outputFilePath = path.resolve(args[args.length - 1])
const inputFilePaths = args.slice(0, -1).map((p) => path.resolve(p))
extractSectionsFromOpenApi(inputFilePaths, outputFilePath)