mirror of
https://github.com/apache/superset.git
synced 2026-04-07 10:31:50 +00:00
177 lines
5.1 KiB
JavaScript
177 lines
5.1 KiB
JavaScript
/**
|
|
* Licensed to the Apache Software Foundation (ASF) under one
|
|
* or more contributor license agreements. See the NOTICE file
|
|
* distributed with this work for additional information
|
|
* regarding copyright ownership. The ASF licenses this file
|
|
* to you under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance
|
|
* with the License. You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing,
|
|
* software distributed under the License is distributed on an
|
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
|
* KIND, either express or implied. See the License for the
|
|
* specific language governing permissions and limitations
|
|
* under the License.
|
|
*/
|
|
|
|
/**
|
|
* Replaces auto-generated tag pages (DocCardList cards) with endpoint tables
|
|
* showing HTTP method, endpoint name, and URI path for each endpoint in the tag.
|
|
*
|
|
* Runs after `docusaurus gen-api-docs` and `convert-api-sidebar.mjs`.
|
|
* Uses the generated sidebar to get correct endpoint slugs.
|
|
*/
|
|
|
|
import fs from 'fs';
|
|
import path from 'path';
|
|
import { createRequire } from 'module';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
const require = createRequire(import.meta.url);
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
|
|
const SPEC_PATH = path.join(__dirname, '..', 'static', 'resources', 'openapi.json');
|
|
const API_DOCS_DIR = path.join(__dirname, '..', 'docs', 'api');
|
|
const SIDEBAR_PATH = path.join(API_DOCS_DIR, 'sidebar.js');
|
|
|
|
function slugify(text) {
|
|
return text
|
|
.toLowerCase()
|
|
.replace(/[^a-z0-9]+/g, '-')
|
|
.replace(/(^-|-$)/g, '');
|
|
}
|
|
|
|
/**
|
|
* Build a map from sidebar label → doc slug by reading the generated sidebar.
|
|
*/
|
|
function buildSlugMap() {
|
|
const labelToSlug = {};
|
|
|
|
try {
|
|
const sidebar = require(SIDEBAR_PATH);
|
|
|
|
const extractDocs = (items) => {
|
|
for (const item of items) {
|
|
if (item.type === 'doc' && item.label && item.id) {
|
|
const slug = item.id.replace(/^api\//, '');
|
|
labelToSlug[item.label] = slug;
|
|
}
|
|
if (item.items) extractDocs(item.items);
|
|
}
|
|
};
|
|
|
|
extractDocs(sidebar);
|
|
} catch {
|
|
console.warn('Could not read sidebar, will use computed slugs');
|
|
}
|
|
|
|
return labelToSlug;
|
|
}
|
|
|
|
function main() {
|
|
console.log('Generating API tag pages with endpoint tables...');
|
|
|
|
const spec = JSON.parse(fs.readFileSync(SPEC_PATH, 'utf-8'));
|
|
const labelToSlug = buildSlugMap();
|
|
|
|
// Build tag descriptions from the spec
|
|
const tagDescriptions = {};
|
|
for (const tag of spec.tags || []) {
|
|
tagDescriptions[tag.name] = tag.description || '';
|
|
}
|
|
|
|
// Build tag → endpoints map
|
|
const tagEndpoints = {};
|
|
for (const [pathUrl, methods] of Object.entries(spec.paths || {})) {
|
|
for (const [method, details] of Object.entries(methods)) {
|
|
if (!['get', 'post', 'put', 'delete', 'patch'].includes(method)) continue;
|
|
|
|
const tags = details.tags || ['Untagged'];
|
|
const summary = details.summary || `${method.toUpperCase()} ${pathUrl}`;
|
|
const slug = labelToSlug[summary] || slugify(summary);
|
|
|
|
for (const tag of tags) {
|
|
if (!tagEndpoints[tag]) {
|
|
tagEndpoints[tag] = [];
|
|
}
|
|
tagEndpoints[tag].push({
|
|
method: method.toUpperCase(),
|
|
path: pathUrl,
|
|
summary,
|
|
slug,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Sort endpoints within each tag by path then method
|
|
for (const tag of Object.keys(tagEndpoints)) {
|
|
tagEndpoints[tag].sort((a, b) =>
|
|
a.path.localeCompare(b.path) || a.method.localeCompare(b.method)
|
|
);
|
|
}
|
|
|
|
// Scan existing .tag.mdx files and match by frontmatter title
|
|
const tagFiles = fs.readdirSync(API_DOCS_DIR)
|
|
.filter(f => f.endsWith('.tag.mdx'));
|
|
|
|
let updated = 0;
|
|
for (const tagFile of tagFiles) {
|
|
const tagFilePath = path.join(API_DOCS_DIR, tagFile);
|
|
const existing = fs.readFileSync(tagFilePath, 'utf-8');
|
|
|
|
// Extract frontmatter
|
|
const frontmatterMatch = existing.match(/^---\n([\s\S]*?)\n---/);
|
|
if (!frontmatterMatch) {
|
|
console.warn(` No frontmatter in ${tagFile}, skipping`);
|
|
continue;
|
|
}
|
|
|
|
const frontmatter = frontmatterMatch[1];
|
|
|
|
// Extract the title from frontmatter (this matches the spec tag name)
|
|
const titleMatch = frontmatter.match(/title:\s*"([^"]+)"/);
|
|
if (!titleMatch) {
|
|
console.warn(` No title in ${tagFile}, skipping`);
|
|
continue;
|
|
}
|
|
|
|
const tagName = titleMatch[1];
|
|
const endpoints = tagEndpoints[tagName];
|
|
|
|
if (!endpoints || endpoints.length === 0) {
|
|
console.warn(` No endpoints found for tag "${tagName}" (${tagFile})`);
|
|
continue;
|
|
}
|
|
|
|
const description = tagDescriptions[tagName] || '';
|
|
|
|
// Build the endpoint table
|
|
let table = '| Method | Endpoint | Path |\n';
|
|
table += '|--------|----------|------|\n';
|
|
for (const ep of endpoints) {
|
|
table += `| \`${ep.method}\` | [${ep.summary}](./${ep.slug}) | \`${ep.path}\` |\n`;
|
|
}
|
|
|
|
// Generate the new MDX content
|
|
const mdx = `---
|
|
${frontmatter}
|
|
---
|
|
|
|
${description}
|
|
|
|
${table}
|
|
`;
|
|
|
|
fs.writeFileSync(tagFilePath, mdx);
|
|
updated++;
|
|
}
|
|
|
|
console.log(`Updated ${updated} tag pages with endpoint tables`);
|
|
}
|
|
|
|
main();
|