diff --git a/api-docs/influxdb3/core/v3/ref.yml b/api-docs/influxdb3/core/v3/ref.yml
index a8d7c5469..e027f5af6 100644
--- a/api-docs/influxdb3/core/v3/ref.yml
+++ b/api-docs/influxdb3/core/v3/ref.yml
@@ -189,7 +189,7 @@ tags:
description: Retrieve server metrics, status, and version information
- name: Table
description: Manage table schemas and data
- - name: Token
+ - name: Auth token
description: Manage tokens for authentication and authorization
- name: Write data
description: |
@@ -1169,7 +1169,6 @@ paths:
description: Creates a distinct cache for a table.
tags:
- Cache data
- - Table
requestBody:
required: true
content:
@@ -1215,7 +1214,6 @@ paths:
description: Cache not found.
tags:
- Cache data
- - Table
/api/v3/configure/last_cache:
post:
operationId: PostConfigureLastCache
@@ -1240,7 +1238,6 @@ paths:
description: Cache already exists.
tags:
- Cache data
- - Table
delete:
operationId: DeleteConfigureLastCache
summary: Delete last cache
@@ -1270,7 +1267,6 @@ paths:
description: Cache not found.
tags:
- Cache data
- - Table
/api/v3/configure/processing_engine_trigger:
post:
operationId: PostConfigureProcessingEngineTrigger
@@ -1712,7 +1708,7 @@ paths:
$ref: '#/components/responses/Unauthorized'
tags:
- Authentication
- - Token
+ - Auth token
/api/v3/configure/token/admin/regenerate:
post:
operationId: PostRegenerateAdminToken
@@ -1731,7 +1727,7 @@ paths:
$ref: '#/components/responses/Unauthorized'
tags:
- Authentication
- - Token
+ - Auth token
/api/v3/configure/token:
delete:
operationId: DeleteToken
@@ -1755,7 +1751,7 @@ paths:
description: Token not found.
tags:
- Authentication
- - Token
+ - Auth token
/api/v3/configure/token/named_admin:
post:
operationId: PostCreateNamedAdminToken
@@ -1786,7 +1782,7 @@ paths:
description: A token with this name already exists.
tags:
- Authentication
- - Token
+ - Auth token
/api/v3/plugins/files:
put:
operationId: PutPluginFile
diff --git a/api-docs/influxdb3/enterprise/v3/ref.yml b/api-docs/influxdb3/enterprise/v3/ref.yml
index b68a86a55..26236d7cd 100644
--- a/api-docs/influxdb3/enterprise/v3/ref.yml
+++ b/api-docs/influxdb3/enterprise/v3/ref.yml
@@ -158,7 +158,7 @@ tags:
description: Retrieve server metrics, status, and version information
- name: Table
description: Manage table schemas and data
- - name: Token
+ - name: Auth token
description: Manage tokens for authentication and authorization
- name: Write data
description: |
@@ -1191,7 +1191,6 @@ paths:
description: Creates a distinct cache for a table.
tags:
- Cache data
- - Table
requestBody:
required: true
content:
@@ -1236,7 +1235,6 @@ paths:
description: Cache not found.
tags:
- Cache data
- - Table
/api/v3/configure/last_cache:
post:
operationId: PostConfigureLastCache
@@ -1261,7 +1259,6 @@ paths:
description: Cache already exists.
tags:
- Cache data
- - Table
delete:
operationId: DeleteConfigureLastCache
summary: Delete last cache
@@ -1291,7 +1288,6 @@ paths:
description: Cache not found.
tags:
- Cache data
- - Table
/api/v3/configure/processing_engine_trigger:
post:
operationId: PostConfigureProcessingEngineTrigger
@@ -1732,7 +1728,7 @@ paths:
$ref: '#/components/responses/Unauthorized'
tags:
- Authentication
- - Token
+ - Auth token
/api/v3/configure/token/admin:
post:
operationId: PostCreateAdminToken
@@ -1753,7 +1749,7 @@ paths:
$ref: '#/components/responses/Unauthorized'
tags:
- Authentication
- - Token
+ - Auth token
/api/v3/configure/token/admin/regenerate:
post:
operationId: PostRegenerateAdminToken
@@ -1772,7 +1768,7 @@ paths:
$ref: '#/components/responses/Unauthorized'
tags:
- Authentication
- - Token
+ - Auth token
/api/v3/configure/token:
delete:
operationId: DeleteToken
@@ -1795,7 +1791,7 @@ paths:
description: Token not found.
tags:
- Authentication
- - Token
+ - Auth token
/api/v3/configure/token/named_admin:
post:
operationId: PostCreateNamedAdminToken
@@ -1825,7 +1821,7 @@ paths:
description: A token with this name already exists.
tags:
- Authentication
- - Token
+ - Auth token
/api/v3/plugins/files:
put:
operationId: PutPluginFile
diff --git a/api-docs/scripts/dist/generate-openapi-articles.js b/api-docs/scripts/dist/generate-openapi-articles.js
index 828f8aacd..e6a185d30 100644
--- a/api-docs/scripts/dist/generate-openapi-articles.js
+++ b/api-docs/scripts/dist/generate-openapi-articles.js
@@ -1,5 +1,5 @@
#!/usr/bin/env node
-'use strict';
+"use strict";
/**
* Generate OpenAPI Articles Script
*
@@ -20,70 +20,47 @@
*
* @module generate-openapi-articles
*/
-var __createBinding =
- (this && this.__createBinding) ||
- (Object.create
- ? function (o, m, k, k2) {
- if (k2 === undefined) k2 = k;
- var desc = Object.getOwnPropertyDescriptor(m, k);
- if (
- !desc ||
- ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
- ) {
- desc = {
- enumerable: true,
- get: function () {
- return m[k];
- },
- };
- }
- Object.defineProperty(o, k2, desc);
- }
- : function (o, m, k, k2) {
- if (k2 === undefined) k2 = k;
- o[k2] = m[k];
- });
-var __setModuleDefault =
- (this && this.__setModuleDefault) ||
- (Object.create
- ? function (o, v) {
- Object.defineProperty(o, 'default', { enumerable: true, value: v });
- }
- : function (o, v) {
- o['default'] = v;
- });
-var __importStar =
- (this && this.__importStar) ||
- (function () {
- var ownKeys = function (o) {
- ownKeys =
- Object.getOwnPropertyNames ||
- function (o) {
- var ar = [];
- for (var k in o)
- if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
- return ar;
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+ o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || (function () {
+ var ownKeys = function(o) {
+ ownKeys = Object.getOwnPropertyNames || function (o) {
+ var ar = [];
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
+ return ar;
};
- return ownKeys(o);
+ return ownKeys(o);
};
return function (mod) {
- if (mod && mod.__esModule) return mod;
- var result = {};
- if (mod != null)
- for (var k = ownKeys(mod), i = 0; i < k.length; i++)
- if (k[i] !== 'default') __createBinding(result, mod, k[i]);
- __setModuleDefault(result, mod);
- return result;
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
+ __setModuleDefault(result, mod);
+ return result;
};
- })();
-Object.defineProperty(exports, '__esModule', { value: true });
+})();
+Object.defineProperty(exports, "__esModule", { value: true });
exports.productConfigs = void 0;
exports.processProduct = processProduct;
exports.generateDataFromOpenAPI = generateDataFromOpenAPI;
exports.generatePagesFromArticleData = generatePagesFromArticleData;
-const child_process_1 = require('child_process');
-const path = __importStar(require('path'));
-const fs = __importStar(require('fs'));
+const child_process_1 = require("child_process");
+const path = __importStar(require("path"));
+const fs = __importStar(require("fs"));
// Import the OpenAPI to Hugo converter
const openapiPathsToHugo = require('./openapi-paths-to-hugo-data/index.js');
// Calculate the relative paths
@@ -97,19 +74,20 @@ const API_DOCS_ROOT = 'api-docs';
* @throws Exits process with code 1 on error
*/
function execCommand(command, description) {
- try {
- if (description) {
- console.log(`\n${description}...`);
+ try {
+ if (description) {
+ console.log(`\n${description}...`);
+ }
+ console.log(`Executing: ${command}\n`);
+ (0, child_process_1.execSync)(command, { stdio: 'inherit' });
}
- console.log(`Executing: ${command}\n`);
- (0, child_process_1.execSync)(command, { stdio: 'inherit' });
- } catch (error) {
- console.error(`\n❌ Error executing command: ${command}`);
- if (error instanceof Error) {
- console.error(error.message);
+ catch (error) {
+ console.error(`\n❌ Error executing command: ${command}`);
+ if (error instanceof Error) {
+ console.error(error.message);
+ }
+ process.exit(1);
}
- process.exit(1);
- }
}
/**
* Generate a clean static directory name from a product key.
@@ -119,12 +97,12 @@ function execCommand(command, description) {
* @returns Clean directory name (e.g., 'influxdb-cloud-v2', 'influxdb3-core')
*/
function getStaticDirName(productKey) {
- // For influxdb3_* products, convert underscore to hyphen and don't add prefix
- if (productKey.startsWith('influxdb3_')) {
- return productKey.replace('_', '-');
- }
- // For other products, add 'influxdb-' prefix
- return `influxdb-${productKey}`;
+ // For influxdb3_* products, convert underscore to hyphen and don't add prefix
+ if (productKey.startsWith('influxdb3_')) {
+ return productKey.replace('_', '-');
+ }
+ // For other products, add 'influxdb-' prefix
+ return `influxdb-${productKey}`;
}
/**
* Generate Hugo data files from OpenAPI specification
@@ -134,14 +112,14 @@ function getStaticDirName(productKey) {
* @param articleOutPath - Output path for article metadata
*/
function generateDataFromOpenAPI(specFile, dataOutPath, articleOutPath) {
- if (!fs.existsSync(dataOutPath)) {
- fs.mkdirSync(dataOutPath, { recursive: true });
- }
- openapiPathsToHugo.generateHugoData({
- dataOutPath,
- articleOutPath,
- specFile,
- });
+ if (!fs.existsSync(dataOutPath)) {
+ fs.mkdirSync(dataOutPath, { recursive: true });
+ }
+ openapiPathsToHugo.generateHugoData({
+ dataOutPath,
+ articleOutPath,
+ specFile,
+ });
}
/**
* Generate Hugo content pages from article data
@@ -152,122 +130,107 @@ function generateDataFromOpenAPI(specFile, dataOutPath, articleOutPath) {
* @param options - Generation options
*/
function generatePagesFromArticleData(options) {
- const {
- articlesPath,
- contentPath,
- menuKey,
- menuParent,
- productDescription,
- skipParentMenu,
- } = options;
- const yaml = require('js-yaml');
- const articlesFile = path.join(articlesPath, 'articles.yml');
- if (!fs.existsSync(articlesFile)) {
- console.warn(`⚠️ Articles file not found: ${articlesFile}`);
- return;
- }
- // Read articles data
- const articlesContent = fs.readFileSync(articlesFile, 'utf8');
- const data = yaml.load(articlesContent);
- if (!data.articles || !Array.isArray(data.articles)) {
- console.warn(`⚠️ No articles found in ${articlesFile}`);
- return;
- }
- // Ensure content directory exists
- if (!fs.existsSync(contentPath)) {
- fs.mkdirSync(contentPath, { recursive: true });
- }
- // Determine the API parent directory from the first article's path
- // e.g., if article path is "api/v1/health", the API root is "api"
- const firstArticlePath = data.articles[0]?.path || '';
- const apiRootDir = firstArticlePath.split('/')[0];
- // Generate parent _index.md for the API section
- if (apiRootDir) {
- const apiParentDir = path.join(contentPath, apiRootDir);
- const parentIndexFile = path.join(apiParentDir, '_index.md');
- if (!fs.existsSync(apiParentDir)) {
- fs.mkdirSync(apiParentDir, { recursive: true });
+ const { articlesPath, contentPath, menuKey, menuParent, productDescription, skipParentMenu, } = options;
+ const yaml = require('js-yaml');
+ const articlesFile = path.join(articlesPath, 'articles.yml');
+ if (!fs.existsSync(articlesFile)) {
+ console.warn(`⚠️ Articles file not found: ${articlesFile}`);
+ return;
}
- if (!fs.existsSync(parentIndexFile)) {
- const parentFrontmatter = {
- title: menuParent || 'HTTP API',
- description:
- productDescription ||
- 'API reference documentation for all available endpoints.',
- weight: 104,
- };
- // Add menu entry for parent page (unless skipParentMenu is true)
- if (menuKey && !skipParentMenu) {
- parentFrontmatter.menu = {
- [menuKey]: {
- name: menuParent || 'HTTP API',
- },
- };
- }
- const parentContent = `---
+ // Read articles data
+ const articlesContent = fs.readFileSync(articlesFile, 'utf8');
+ const data = yaml.load(articlesContent);
+ if (!data.articles || !Array.isArray(data.articles)) {
+ console.warn(`⚠️ No articles found in ${articlesFile}`);
+ return;
+ }
+ // Ensure content directory exists
+ if (!fs.existsSync(contentPath)) {
+ fs.mkdirSync(contentPath, { recursive: true });
+ }
+ // Determine the API parent directory from the first article's path
+ // e.g., if article path is "api/v1/health", the API root is "api"
+ const firstArticlePath = data.articles[0]?.path || '';
+ const apiRootDir = firstArticlePath.split('/')[0];
+ // Generate parent _index.md for the API section
+ if (apiRootDir) {
+ const apiParentDir = path.join(contentPath, apiRootDir);
+ const parentIndexFile = path.join(apiParentDir, '_index.md');
+ if (!fs.existsSync(apiParentDir)) {
+ fs.mkdirSync(apiParentDir, { recursive: true });
+ }
+ if (!fs.existsSync(parentIndexFile)) {
+ const parentFrontmatter = {
+ title: menuParent || 'HTTP API',
+ description: productDescription ||
+ 'API reference documentation for all available endpoints.',
+ weight: 104,
+ };
+ // Add menu entry for parent page (unless skipParentMenu is true)
+ if (menuKey && !skipParentMenu) {
+ parentFrontmatter.menu = {
+ [menuKey]: {
+ name: menuParent || 'HTTP API',
+ },
+ };
+ }
+ const parentContent = `---
${yaml.dump(parentFrontmatter)}---
`;
- fs.writeFileSync(parentIndexFile, parentContent);
- console.log(`✓ Generated parent index at ${parentIndexFile}`);
+ fs.writeFileSync(parentIndexFile, parentContent);
+ console.log(`✓ Generated parent index at ${parentIndexFile}`);
+ }
}
- }
- // Generate a page for each article
- for (const article of data.articles) {
- const pagePath = path.join(contentPath, article.path);
- const pageFile = path.join(pagePath, '_index.md');
- // Create directory if needed
- if (!fs.existsSync(pagePath)) {
- fs.mkdirSync(pagePath, { recursive: true });
- }
- // Build frontmatter object
- // Use menuName for display (actual endpoint path like /health)
- // Fall back to name or path if menuName is not set
- const displayName =
- article.fields.menuName || article.fields.name || article.path;
- const frontmatter = {
- title: displayName,
- description: `API reference for ${displayName}`,
- type: 'api',
- // Use explicit layout to override Hugo's default section template lookup
- // (Hugo's section lookup ignores `type`, so we need `layout` for the 3-column API layout)
- layout: 'list',
- staticFilePath: article.fields.staticFilePath,
- weight: 100,
- };
- // Add menu entry if menuKey is provided
- // Use menuName for menu display (shows actual endpoint path like /health)
- if (menuKey) {
- frontmatter.menu = {
- [menuKey]: {
- name: displayName,
- ...(menuParent && { parent: menuParent }),
- },
- };
- }
- // Add related links if present in article fields
- if (
- article.fields.related &&
- Array.isArray(article.fields.related) &&
- article.fields.related.length > 0
- ) {
- frontmatter.related = article.fields.related;
- }
- // Add OpenAPI tags if present in article fields (for frontmatter metadata)
- if (
- article.fields.apiTags &&
- Array.isArray(article.fields.apiTags) &&
- article.fields.apiTags.length > 0
- ) {
- frontmatter.api_tags = article.fields.apiTags;
- }
- const pageContent = `---
+ // Generate a page for each article
+ for (const article of data.articles) {
+ const pagePath = path.join(contentPath, article.path);
+ const pageFile = path.join(pagePath, '_index.md');
+ // Create directory if needed
+ if (!fs.existsSync(pagePath)) {
+ fs.mkdirSync(pagePath, { recursive: true });
+ }
+ // Build frontmatter object
+ // Use menuName for display (actual endpoint path like /health)
+ // Fall back to name or path if menuName is not set
+ const displayName = article.fields.menuName || article.fields.name || article.path;
+ const frontmatter = {
+ title: displayName,
+ description: `API reference for ${displayName}`,
+ type: 'api',
+ // Use explicit layout to override Hugo's default section template lookup
+ // (Hugo's section lookup ignores `type`, so we need `layout` for the 3-column API layout)
+ layout: 'list',
+ staticFilePath: article.fields.staticFilePath,
+ weight: 100,
+ };
+ // Add menu entry if menuKey is provided
+ // Use menuName for menu display (shows actual endpoint path like /health)
+ if (menuKey) {
+ frontmatter.menu = {
+ [menuKey]: {
+ name: displayName,
+ ...(menuParent && { parent: menuParent }),
+ },
+ };
+ }
+ // Add related links if present in article fields
+ if (article.fields.related &&
+ Array.isArray(article.fields.related) &&
+ article.fields.related.length > 0) {
+ frontmatter.related = article.fields.related;
+ }
+ // Add OpenAPI tags if present in article fields (for frontmatter metadata)
+ if (article.fields.apiTags &&
+ Array.isArray(article.fields.apiTags) &&
+ article.fields.apiTags.length > 0) {
+ frontmatter.api_tags = article.fields.apiTags;
+ }
+ const pageContent = `---
${yaml.dump(frontmatter)}---
`;
- fs.writeFileSync(pageFile, pageContent);
- }
- console.log(
- `✓ Generated ${data.articles.length} content pages in ${contentPath}`
- );
+ fs.writeFileSync(pageFile, pageContent);
+ }
+ console.log(`✓ Generated ${data.articles.length} content pages in ${contentPath}`);
}
/**
* Generate Hugo content pages from tag-based article data
@@ -279,119 +242,105 @@ ${yaml.dump(frontmatter)}---
* @param options - Generation options
*/
function generateTagPagesFromArticleData(options) {
- const {
- articlesPath,
- contentPath,
- menuKey,
- menuParent,
- productDescription,
- skipParentMenu,
- } = options;
- const yaml = require('js-yaml');
- const articlesFile = path.join(articlesPath, 'articles.yml');
- if (!fs.existsSync(articlesFile)) {
- console.warn(`⚠️ Articles file not found: ${articlesFile}`);
- return;
- }
- // Read articles data
- const articlesContent = fs.readFileSync(articlesFile, 'utf8');
- const data = yaml.load(articlesContent);
- if (!data.articles || !Array.isArray(data.articles)) {
- console.warn(`⚠️ No articles found in ${articlesFile}`);
- return;
- }
- // Ensure content directory exists
- if (!fs.existsSync(contentPath)) {
- fs.mkdirSync(contentPath, { recursive: true });
- }
- // Generate parent _index.md for the API section
- const apiParentDir = path.join(contentPath, 'api');
- const parentIndexFile = path.join(apiParentDir, '_index.md');
- if (!fs.existsSync(apiParentDir)) {
- fs.mkdirSync(apiParentDir, { recursive: true });
- }
- if (!fs.existsSync(parentIndexFile)) {
- const parentFrontmatter = {
- title: menuParent || 'HTTP API',
- description:
- productDescription ||
- 'API reference documentation for all available endpoints.',
- weight: 104,
- };
- // Add menu entry for parent page (unless skipParentMenu is true)
- if (menuKey && !skipParentMenu) {
- parentFrontmatter.menu = {
- [menuKey]: {
- name: menuParent || 'HTTP API',
- },
- };
+ const { articlesPath, contentPath, menuKey, menuParent, productDescription, skipParentMenu, } = options;
+ const yaml = require('js-yaml');
+ const articlesFile = path.join(articlesPath, 'articles.yml');
+ if (!fs.existsSync(articlesFile)) {
+ console.warn(`⚠️ Articles file not found: ${articlesFile}`);
+ return;
}
- const parentContent = `---
+ // Read articles data
+ const articlesContent = fs.readFileSync(articlesFile, 'utf8');
+ const data = yaml.load(articlesContent);
+ if (!data.articles || !Array.isArray(data.articles)) {
+ console.warn(`⚠️ No articles found in ${articlesFile}`);
+ return;
+ }
+ // Ensure content directory exists
+ if (!fs.existsSync(contentPath)) {
+ fs.mkdirSync(contentPath, { recursive: true });
+ }
+ // Generate parent _index.md for the API section
+ const apiParentDir = path.join(contentPath, 'api');
+ const parentIndexFile = path.join(apiParentDir, '_index.md');
+ if (!fs.existsSync(apiParentDir)) {
+ fs.mkdirSync(apiParentDir, { recursive: true });
+ }
+ if (!fs.existsSync(parentIndexFile)) {
+ const parentFrontmatter = {
+ title: menuParent || 'HTTP API',
+ description: productDescription ||
+ 'API reference documentation for all available endpoints.',
+ weight: 104,
+ };
+ // Add menu entry for parent page (unless skipParentMenu is true)
+ if (menuKey && !skipParentMenu) {
+ parentFrontmatter.menu = {
+ [menuKey]: {
+ name: menuParent || 'HTTP API',
+ },
+ };
+ }
+ const parentContent = `---
${yaml.dump(parentFrontmatter)}---
`;
- fs.writeFileSync(parentIndexFile, parentContent);
- console.log(`✓ Generated parent index at ${parentIndexFile}`);
- }
- // Generate a page for each article (tag)
- for (const article of data.articles) {
- const pagePath = path.join(contentPath, article.path);
- const pageFile = path.join(pagePath, '_index.md');
- // Create directory if needed
- if (!fs.existsSync(pagePath)) {
- fs.mkdirSync(pagePath, { recursive: true });
+ fs.writeFileSync(parentIndexFile, parentContent);
+ console.log(`✓ Generated parent index at ${parentIndexFile}`);
}
- // Build frontmatter object
- const title = article.fields.title || article.fields.name || article.path;
- const isConceptual = article.fields.isConceptual === true;
- const frontmatter = {
- title,
- description: article.fields.description || `API reference for ${title}`,
- type: 'api',
- layout: isConceptual ? 'single' : 'list',
- staticFilePath: article.fields.staticFilePath,
- weight: 100,
- // Tag-based fields
- tag: article.fields.tag,
- isConceptual,
- menuGroup: article.fields.menuGroup,
- };
- // Add operations for TOC generation (only for non-conceptual pages)
- if (
- !isConceptual &&
- article.fields.operations &&
- article.fields.operations.length > 0
- ) {
- frontmatter.operations = article.fields.operations;
- }
- // Add tag description for conceptual pages
- if (isConceptual && article.fields.tagDescription) {
- frontmatter.tagDescription = article.fields.tagDescription;
- }
- // Note: We deliberately don't add menu entries for tag-based API pages.
- // The API sidebar navigation (api/sidebar-nav.html) handles navigation
- // for API reference pages, avoiding conflicts with existing menu items
- // like "Query data" and "Write data" that exist in the main sidebar.
- // Add related links if present in article fields
- if (
- article.fields.related &&
- Array.isArray(article.fields.related) &&
- article.fields.related.length > 0
- ) {
- frontmatter.related = article.fields.related;
- }
- const pageContent = `---
+ // Generate a page for each article (tag)
+ for (const article of data.articles) {
+ const pagePath = path.join(contentPath, article.path);
+ const pageFile = path.join(pagePath, '_index.md');
+ // Create directory if needed
+ if (!fs.existsSync(pagePath)) {
+ fs.mkdirSync(pagePath, { recursive: true });
+ }
+ // Build frontmatter object
+ const title = article.fields.title || article.fields.name || article.path;
+ const isConceptual = article.fields.isConceptual === true;
+ const frontmatter = {
+ title,
+ description: article.fields.description || `API reference for ${title}`,
+ type: 'api',
+ layout: isConceptual ? 'single' : 'list',
+ staticFilePath: article.fields.staticFilePath,
+ weight: 100,
+ // Tag-based fields
+ tag: article.fields.tag,
+ isConceptual,
+ menuGroup: article.fields.menuGroup,
+ };
+ // Add operations for TOC generation (only for non-conceptual pages)
+ if (!isConceptual &&
+ article.fields.operations &&
+ article.fields.operations.length > 0) {
+ frontmatter.operations = article.fields.operations;
+ }
+ // Add tag description for conceptual pages
+ if (isConceptual && article.fields.tagDescription) {
+ frontmatter.tagDescription = article.fields.tagDescription;
+ }
+ // Note: We deliberately don't add menu entries for tag-based API pages.
+ // The API sidebar navigation (api/sidebar-nav.html) handles navigation
+ // for API reference pages, avoiding conflicts with existing menu items
+ // like "Query data" and "Write data" that exist in the main sidebar.
+ // Add related links if present in article fields
+ if (article.fields.related &&
+ Array.isArray(article.fields.related) &&
+ article.fields.related.length > 0) {
+ frontmatter.related = article.fields.related;
+ }
+ const pageContent = `---
${yaml.dump(frontmatter)}---
`;
- fs.writeFileSync(pageFile, pageContent);
- }
- console.log(
- `✓ Generated ${data.articles.length} tag-based content pages in ${contentPath}`
- );
- // Generate individual operation pages for standalone URLs
- generateOperationPages({
- articlesPath,
- contentPath,
- });
+ fs.writeFileSync(pageFile, pageContent);
+ }
+ console.log(`✓ Generated ${data.articles.length} tag-based content pages in ${contentPath}`);
+ // Generate individual operation pages for standalone URLs
+ generateOperationPages({
+ articlesPath,
+ contentPath,
+ });
}
/**
* Convert API path to URL-safe slug
@@ -403,8 +352,8 @@ ${yaml.dump(frontmatter)}---
* @returns URL-safe path slug (e.g., "write", "api/v3/write_lp")
*/
function apiPathToSlug(apiPath) {
- // Remove leading slash, keep underscores (they're URL-safe)
- return apiPath.replace(/^\//, '');
+ // Remove leading slash, keep underscores (they're URL-safe)
+ return apiPath.replace(/^\//, '');
}
/**
* Generate standalone Hugo content pages for each API operation
@@ -415,76 +364,75 @@ function apiPathToSlug(apiPath) {
* @param options - Generation options
*/
function generateOperationPages(options) {
- const { articlesPath, contentPath } = options;
- const yaml = require('js-yaml');
- const articlesFile = path.join(articlesPath, 'articles.yml');
- if (!fs.existsSync(articlesFile)) {
- console.warn(`⚠️ Articles file not found: ${articlesFile}`);
- return;
- }
- // Read articles data
- const articlesContent = fs.readFileSync(articlesFile, 'utf8');
- const data = yaml.load(articlesContent);
- if (!data.articles || !Array.isArray(data.articles)) {
- console.warn(`⚠️ No articles found in ${articlesFile}`);
- return;
- }
- let operationCount = 0;
- // Process each article (tag) and generate pages for its operations
- for (const article of data.articles) {
- // Skip conceptual articles (they don't have operations)
- if (article.fields.isConceptual) {
- continue;
+ const { articlesPath, contentPath } = options;
+ const yaml = require('js-yaml');
+ const articlesFile = path.join(articlesPath, 'articles.yml');
+ if (!fs.existsSync(articlesFile)) {
+ console.warn(`⚠️ Articles file not found: ${articlesFile}`);
+ return;
}
- const operations = article.fields.operations || [];
- const tagSpecFile = article.fields.staticFilePath;
- const tagName = article.fields.tag || article.fields.name || '';
- for (const op of operations) {
- // Build operation page path: api/{path}/{method}/
- // e.g., /write -> api/write/post/
- // e.g., /api/v3/write_lp -> api/api/v3/write_lp/post/
- const pathSlug = apiPathToSlug(op.path);
- const method = op.method.toLowerCase();
- const operationDir = path.join(contentPath, 'api', pathSlug, method);
- const operationFile = path.join(operationDir, '_index.md');
- // Create directory if needed
- if (!fs.existsSync(operationDir)) {
- fs.mkdirSync(operationDir, { recursive: true });
- }
- // Build frontmatter
- const title = op.summary || `${op.method} ${op.path}`;
- const frontmatter = {
- title,
- description: `API reference for ${op.method} ${op.path}`,
- type: 'api-operation',
- layout: 'operation',
- // RapiDoc Mini configuration
- specFile: tagSpecFile,
- matchPaths: `${method} ${op.path}`,
- // Operation metadata
- operationId: op.operationId,
- method: op.method,
- apiPath: op.path,
- tag: tagName,
- };
- // Add compatibility version if present
- if (op.compatVersion) {
- frontmatter.compatVersion = op.compatVersion;
- }
- // Add related links from operation's externalDocs
- if (op.externalDocs?.url) {
- frontmatter.related = [op.externalDocs.url];
- }
- const pageContent = `---
+ // Read articles data
+ const articlesContent = fs.readFileSync(articlesFile, 'utf8');
+ const data = yaml.load(articlesContent);
+ if (!data.articles || !Array.isArray(data.articles)) {
+ console.warn(`⚠️ No articles found in ${articlesFile}`);
+ return;
+ }
+ let operationCount = 0;
+ // Process each article (tag) and generate pages for its operations
+ for (const article of data.articles) {
+ // Skip conceptual articles (they don't have operations)
+ if (article.fields.isConceptual) {
+ continue;
+ }
+ const operations = article.fields.operations || [];
+ const tagSpecFile = article.fields.staticFilePath;
+ const tagName = article.fields.tag || article.fields.name || '';
+ for (const op of operations) {
+ // Build operation page path: api/{path}/{method}/
+ // e.g., /write -> api/write/post/
+ // e.g., /api/v3/write_lp -> api/api/v3/write_lp/post/
+ const pathSlug = apiPathToSlug(op.path);
+ const method = op.method.toLowerCase();
+ const operationDir = path.join(contentPath, 'api', pathSlug, method);
+ const operationFile = path.join(operationDir, '_index.md');
+ // Create directory if needed
+ if (!fs.existsSync(operationDir)) {
+ fs.mkdirSync(operationDir, { recursive: true });
+ }
+ // Build frontmatter
+ const title = op.summary || `${op.method} ${op.path}`;
+ const frontmatter = {
+ title,
+ description: `API reference for ${op.method} ${op.path}`,
+ type: 'api-operation',
+ layout: 'operation',
+ // RapiDoc Mini configuration
+ specFile: tagSpecFile,
+ // RapiDoc match-paths format: "method /path" (e.g., "post /write")
+ matchPaths: `${method} ${op.path}`,
+ // Operation metadata
+ operationId: op.operationId,
+ method: op.method,
+ apiPath: op.path,
+ tag: tagName,
+ };
+ // Add compatibility version if present
+ if (op.compatVersion) {
+ frontmatter.compatVersion = op.compatVersion;
+ }
+ // Add related links from operation's externalDocs
+ if (op.externalDocs?.url) {
+ frontmatter.related = [op.externalDocs.url];
+ }
+ const pageContent = `---
${yaml.dump(frontmatter)}---
`;
- fs.writeFileSync(operationFile, pageContent);
- operationCount++;
+ fs.writeFileSync(operationFile, pageContent);
+ operationCount++;
+ }
}
- }
- console.log(
- `✓ Generated ${operationCount} operation pages in ${contentPath}/api/`
- );
+ console.log(`✓ Generated ${operationCount} operation pages in ${contentPath}/api/`);
}
/**
* Product configurations for all InfluxDB editions
@@ -492,71 +440,62 @@ ${yaml.dump(frontmatter)}---
* Maps product identifiers to their OpenAPI specs and content directories
*/
const productConfigs = {
- // TODO: v2 products (cloud-v2, oss-v2) are disabled for now because they
- // have existing Redoc-based API reference at /reference/api/
- // Uncomment when ready to migrate v2 products to Scalar
- // 'cloud-v2': {
- // specFile: path.join(API_DOCS_ROOT, 'influxdb/cloud/v2/ref.yml'),
- // pagesDir: path.join(DOCS_ROOT, 'content/influxdb/cloud/api'),
- // description: 'InfluxDB Cloud (v2 API)',
- // menuKey: 'influxdb_cloud',
- // },
- // 'oss-v2': {
- // specFile: path.join(API_DOCS_ROOT, 'influxdb/v2/v2/ref.yml'),
- // pagesDir: path.join(DOCS_ROOT, 'content/influxdb/v2/api'),
- // description: 'InfluxDB OSS v2',
- // menuKey: 'influxdb_v2',
- // },
- // InfluxDB 3 products use tag-based generation for better UX
- // Keys use underscores to match Hugo data directory structure
- influxdb3_core: {
- specFile: path.join(API_DOCS_ROOT, 'influxdb3/core/v3/ref.yml'),
- pagesDir: path.join(DOCS_ROOT, 'content/influxdb3/core'),
- description: 'InfluxDB 3 Core',
- menuKey: 'influxdb3_core',
- useTagBasedGeneration: true,
- },
- influxdb3_enterprise: {
- specFile: path.join(API_DOCS_ROOT, 'influxdb3/enterprise/v3/ref.yml'),
- pagesDir: path.join(DOCS_ROOT, 'content/influxdb3/enterprise'),
- description: 'InfluxDB 3 Enterprise',
- menuKey: 'influxdb3_enterprise',
- useTagBasedGeneration: true,
- },
- // Note: Cloud Dedicated, Serverless, and Clustered use management APIs
- // with paths like /accounts/{accountId}/... so we put them under /api/
- // These products have existing /reference/api/ pages with menu entries,
- // so we skip adding menu entries to the generated parent pages.
- 'cloud-dedicated': {
- specFile: path.join(
- API_DOCS_ROOT,
- 'influxdb3/cloud-dedicated/management/openapi.yml'
- ),
- pagesDir: path.join(DOCS_ROOT, 'content/influxdb3/cloud-dedicated/api'),
- description: 'InfluxDB Cloud Dedicated',
- menuKey: 'influxdb3_cloud_dedicated',
- skipParentMenu: true,
- },
- 'cloud-serverless': {
- specFile: path.join(
- API_DOCS_ROOT,
- 'influxdb3/cloud-serverless/management/openapi.yml'
- ),
- pagesDir: path.join(DOCS_ROOT, 'content/influxdb3/cloud-serverless/api'),
- description: 'InfluxDB Cloud Serverless',
- menuKey: 'influxdb3_cloud_serverless',
- skipParentMenu: true,
- },
- clustered: {
- specFile: path.join(
- API_DOCS_ROOT,
- 'influxdb3/clustered/management/openapi.yml'
- ),
- pagesDir: path.join(DOCS_ROOT, 'content/influxdb3/clustered/api'),
- description: 'InfluxDB Clustered',
- menuKey: 'influxdb3_clustered',
- skipParentMenu: true,
- },
+ // TODO: v2 products (cloud-v2, oss-v2) are disabled for now because they
+ // have existing Redoc-based API reference at /reference/api/
+ // Uncomment when ready to migrate v2 products to Scalar
+ // 'cloud-v2': {
+ // specFile: path.join(API_DOCS_ROOT, 'influxdb/cloud/v2/ref.yml'),
+ // pagesDir: path.join(DOCS_ROOT, 'content/influxdb/cloud/api'),
+ // description: 'InfluxDB Cloud (v2 API)',
+ // menuKey: 'influxdb_cloud',
+ // },
+ // 'oss-v2': {
+ // specFile: path.join(API_DOCS_ROOT, 'influxdb/v2/v2/ref.yml'),
+ // pagesDir: path.join(DOCS_ROOT, 'content/influxdb/v2/api'),
+ // description: 'InfluxDB OSS v2',
+ // menuKey: 'influxdb_v2',
+ // },
+ // InfluxDB 3 products use tag-based generation for better UX
+ // Keys use underscores to match Hugo data directory structure
+ influxdb3_core: {
+ specFile: path.join(API_DOCS_ROOT, 'influxdb3/core/v3/ref.yml'),
+ pagesDir: path.join(DOCS_ROOT, 'content/influxdb3/core'),
+ description: 'InfluxDB 3 Core',
+ menuKey: 'influxdb3_core',
+ useTagBasedGeneration: true,
+ },
+ influxdb3_enterprise: {
+ specFile: path.join(API_DOCS_ROOT, 'influxdb3/enterprise/v3/ref.yml'),
+ pagesDir: path.join(DOCS_ROOT, 'content/influxdb3/enterprise'),
+ description: 'InfluxDB 3 Enterprise',
+ menuKey: 'influxdb3_enterprise',
+ useTagBasedGeneration: true,
+ },
+ // Note: Cloud Dedicated, Serverless, and Clustered use management APIs
+ // with paths like /accounts/{accountId}/... so we put them under /api/
+ // These products have existing /reference/api/ pages with menu entries,
+ // so we skip adding menu entries to the generated parent pages.
+ 'cloud-dedicated': {
+ specFile: path.join(API_DOCS_ROOT, 'influxdb3/cloud-dedicated/management/openapi.yml'),
+ pagesDir: path.join(DOCS_ROOT, 'content/influxdb3/cloud-dedicated/api'),
+ description: 'InfluxDB Cloud Dedicated',
+ menuKey: 'influxdb3_cloud_dedicated',
+ skipParentMenu: true,
+ },
+ 'cloud-serverless': {
+ specFile: path.join(API_DOCS_ROOT, 'influxdb3/cloud-serverless/management/openapi.yml'),
+ pagesDir: path.join(DOCS_ROOT, 'content/influxdb3/cloud-serverless/api'),
+ description: 'InfluxDB Cloud Serverless',
+ menuKey: 'influxdb3_cloud_serverless',
+ skipParentMenu: true,
+ },
+ clustered: {
+ specFile: path.join(API_DOCS_ROOT, 'influxdb3/clustered/management/openapi.yml'),
+ pagesDir: path.join(DOCS_ROOT, 'content/influxdb3/clustered/api'),
+ description: 'InfluxDB Clustered',
+ menuKey: 'influxdb3_clustered',
+ skipParentMenu: true,
+ },
};
exports.productConfigs = productConfigs;
/**
@@ -566,140 +505,128 @@ exports.productConfigs = productConfigs;
* @param config - Product configuration
*/
function processProduct(productKey, config) {
- console.log('\n' + '='.repeat(80));
- console.log(`Processing ${config.description || productKey}`);
- console.log('='.repeat(80));
- const staticPath = path.join(DOCS_ROOT, 'static/openapi');
- const staticDirName = getStaticDirName(productKey);
- const staticSpecPath = path.join(staticPath, `${staticDirName}.yml`);
- const staticJsonSpecPath = path.join(staticPath, `${staticDirName}.json`);
- const staticPathsPath = path.join(staticPath, `${staticDirName}/paths`);
- const articlesPath = path.join(
- DOCS_ROOT,
- `data/article_data/influxdb/${productKey}`
- );
- // Check if spec file exists
- if (!fs.existsSync(config.specFile)) {
- console.warn(`⚠️ Spec file not found: ${config.specFile}`);
- console.log('Skipping this product. Run getswagger.sh first if needed.\n');
- return;
- }
- try {
- // Step 1: Execute the getswagger.sh script to fetch/bundle the spec
- // Note: getswagger.sh must run from api-docs/ because it uses relative paths
- const getswaggerScript = path.join(API_DOCS_ROOT, 'getswagger.sh');
- if (fs.existsSync(getswaggerScript)) {
- execCommand(
- `cd ${API_DOCS_ROOT} && ./getswagger.sh ${productKey} -B`,
- `Fetching OpenAPI spec for ${productKey}`
- );
- } else {
- console.log(`⚠️ getswagger.sh not found, skipping fetch step`);
+ console.log('\n' + '='.repeat(80));
+ console.log(`Processing ${config.description || productKey}`);
+ console.log('='.repeat(80));
+ const staticPath = path.join(DOCS_ROOT, 'static/openapi');
+ const staticDirName = getStaticDirName(productKey);
+ const staticSpecPath = path.join(staticPath, `${staticDirName}.yml`);
+ const staticJsonSpecPath = path.join(staticPath, `${staticDirName}.json`);
+ const staticPathsPath = path.join(staticPath, `${staticDirName}/paths`);
+ const articlesPath = path.join(DOCS_ROOT, `data/article_data/influxdb/${productKey}`);
+ // Check if spec file exists
+ if (!fs.existsSync(config.specFile)) {
+ console.warn(`⚠️ Spec file not found: ${config.specFile}`);
+ console.log('Skipping this product. Run getswagger.sh first if needed.\n');
+ return;
}
- // Step 2: Ensure static directory exists
- if (!fs.existsSync(staticPath)) {
- fs.mkdirSync(staticPath, { recursive: true });
+ try {
+ // Step 1: Execute the getswagger.sh script to fetch/bundle the spec
+ // Note: getswagger.sh must run from api-docs/ because it uses relative paths
+ const getswaggerScript = path.join(API_DOCS_ROOT, 'getswagger.sh');
+ if (fs.existsSync(getswaggerScript)) {
+ execCommand(`cd ${API_DOCS_ROOT} && ./getswagger.sh ${productKey} -B`, `Fetching OpenAPI spec for ${productKey}`);
+ }
+ else {
+ console.log(`⚠️ getswagger.sh not found, skipping fetch step`);
+ }
+ // Step 2: Ensure static directory exists
+ if (!fs.existsSync(staticPath)) {
+ fs.mkdirSync(staticPath, { recursive: true });
+ }
+ // Step 3: Copy the generated OpenAPI spec to static folder (YAML)
+ if (fs.existsSync(config.specFile)) {
+ fs.copyFileSync(config.specFile, staticSpecPath);
+ console.log(`✓ Copied spec to ${staticSpecPath}`);
+ // Step 4: Generate JSON version of the spec
+ try {
+ const yaml = require('js-yaml');
+ const specContent = fs.readFileSync(config.specFile, 'utf8');
+ const specObject = yaml.load(specContent);
+ fs.writeFileSync(staticJsonSpecPath, JSON.stringify(specObject, null, 2));
+ console.log(`✓ Generated JSON spec at ${staticJsonSpecPath}`);
+ }
+ catch (jsonError) {
+ console.warn(`⚠️ Could not generate JSON spec: ${jsonError}`);
+ }
+ }
+ // Step 5: Generate Hugo data from OpenAPI spec
+ if (config.useTagBasedGeneration) {
+ // Tag-based generation: group operations by OpenAPI tag
+ const staticTagsPath = path.join(staticPath, `${staticDirName}/tags`);
+ console.log(`\n📋 Using tag-based generation for ${productKey}...`);
+ openapiPathsToHugo.generateHugoDataByTag({
+ specFile: config.specFile,
+ dataOutPath: staticTagsPath,
+ articleOutPath: articlesPath,
+ includePaths: true, // Also generate path-based files for backwards compatibility
+ });
+ // Step 6: Generate Hugo content pages from tag-based article data
+ generateTagPagesFromArticleData({
+ articlesPath,
+ contentPath: config.pagesDir,
+ menuKey: config.menuKey,
+ menuParent: 'InfluxDB HTTP API',
+ skipParentMenu: config.skipParentMenu,
+ });
+ }
+ else {
+ // Path-based generation: group paths by URL prefix (legacy)
+ generateDataFromOpenAPI(config.specFile, staticPathsPath, articlesPath);
+ // Step 6: Generate Hugo content pages from path-based article data
+ generatePagesFromArticleData({
+ articlesPath,
+ contentPath: config.pagesDir,
+ menuKey: config.menuKey,
+ menuParent: 'InfluxDB HTTP API',
+ skipParentMenu: config.skipParentMenu,
+ });
+ }
+ console.log(`\n✅ Successfully processed ${config.description || productKey}\n`);
}
- // Step 3: Copy the generated OpenAPI spec to static folder (YAML)
- if (fs.existsSync(config.specFile)) {
- fs.copyFileSync(config.specFile, staticSpecPath);
- console.log(`✓ Copied spec to ${staticSpecPath}`);
- // Step 4: Generate JSON version of the spec
- try {
- const yaml = require('js-yaml');
- const specContent = fs.readFileSync(config.specFile, 'utf8');
- const specObject = yaml.load(specContent);
- fs.writeFileSync(
- staticJsonSpecPath,
- JSON.stringify(specObject, null, 2)
- );
- console.log(`✓ Generated JSON spec at ${staticJsonSpecPath}`);
- } catch (jsonError) {
- console.warn(`⚠️ Could not generate JSON spec: ${jsonError}`);
- }
+ catch (error) {
+ console.error(`\n❌ Error processing ${productKey}:`, error);
+ process.exit(1);
}
- // Step 5: Generate Hugo data from OpenAPI spec
- if (config.useTagBasedGeneration) {
- // Tag-based generation: group operations by OpenAPI tag
- const staticTagsPath = path.join(staticPath, `${staticDirName}/tags`);
- console.log(`\n📋 Using tag-based generation for ${productKey}...`);
- openapiPathsToHugo.generateHugoDataByTag({
- specFile: config.specFile,
- dataOutPath: staticTagsPath,
- articleOutPath: articlesPath,
- includePaths: true, // Also generate path-based files for backwards compatibility
- });
- // Step 6: Generate Hugo content pages from tag-based article data
- generateTagPagesFromArticleData({
- articlesPath,
- contentPath: config.pagesDir,
- menuKey: config.menuKey,
- menuParent: 'InfluxDB HTTP API',
- skipParentMenu: config.skipParentMenu,
- });
- } else {
- // Path-based generation: group paths by URL prefix (legacy)
- generateDataFromOpenAPI(config.specFile, staticPathsPath, articlesPath);
- // Step 6: Generate Hugo content pages from path-based article data
- generatePagesFromArticleData({
- articlesPath,
- contentPath: config.pagesDir,
- menuKey: config.menuKey,
- menuParent: 'InfluxDB HTTP API',
- skipParentMenu: config.skipParentMenu,
- });
- }
- console.log(
- `\n✅ Successfully processed ${config.description || productKey}\n`
- );
- } catch (error) {
- console.error(`\n❌ Error processing ${productKey}:`, error);
- process.exit(1);
- }
}
/**
* Main execution function
*/
function main() {
- const args = process.argv.slice(2);
- // Determine which products to process
- let productsToProcess;
- if (args.length === 0) {
- // No arguments: process all products
- productsToProcess = Object.keys(productConfigs);
- console.log('\n📋 Processing all products...\n');
- } else {
- // Arguments provided: process only specified products
- productsToProcess = args;
- console.log(
- `\n📋 Processing specified products: ${productsToProcess.join(', ')}\n`
- );
- }
- // Validate product keys
- const invalidProducts = productsToProcess.filter(
- (key) => !productConfigs[key]
- );
- if (invalidProducts.length > 0) {
- console.error(
- `\n❌ Invalid product identifier(s): ${invalidProducts.join(', ')}`
- );
- console.error('\nValid products:');
- Object.keys(productConfigs).forEach((key) => {
- console.error(` - ${key}: ${productConfigs[key].description}`);
+ const args = process.argv.slice(2);
+ // Determine which products to process
+ let productsToProcess;
+ if (args.length === 0) {
+ // No arguments: process all products
+ productsToProcess = Object.keys(productConfigs);
+ console.log('\n📋 Processing all products...\n');
+ }
+ else {
+ // Arguments provided: process only specified products
+ productsToProcess = args;
+ console.log(`\n📋 Processing specified products: ${productsToProcess.join(', ')}\n`);
+ }
+ // Validate product keys
+ const invalidProducts = productsToProcess.filter((key) => !productConfigs[key]);
+ if (invalidProducts.length > 0) {
+ console.error(`\n❌ Invalid product identifier(s): ${invalidProducts.join(', ')}`);
+ console.error('\nValid products:');
+ Object.keys(productConfigs).forEach((key) => {
+ console.error(` - ${key}: ${productConfigs[key].description}`);
+ });
+ process.exit(1);
+ }
+ // Process each product
+ productsToProcess.forEach((productKey) => {
+ const config = productConfigs[productKey];
+ processProduct(productKey, config);
});
- process.exit(1);
- }
- // Process each product
- productsToProcess.forEach((productKey) => {
- const config = productConfigs[productKey];
- processProduct(productKey, config);
- });
- console.log('\n' + '='.repeat(80));
- console.log('✅ All products processed successfully!');
- console.log('='.repeat(80) + '\n');
+ console.log('\n' + '='.repeat(80));
+ console.log('✅ All products processed successfully!');
+ console.log('='.repeat(80) + '\n');
}
// Execute if run directly
if (require.main === module) {
- main();
+ main();
}
-//# sourceMappingURL=generate-openapi-articles.js.map
+//# sourceMappingURL=generate-openapi-articles.js.map
\ No newline at end of file
diff --git a/api-docs/scripts/dist/openapi-paths-to-hugo-data/index.js b/api-docs/scripts/dist/openapi-paths-to-hugo-data/index.js
index f6a4e2e97..9ca311816 100644
--- a/api-docs/scripts/dist/openapi-paths-to-hugo-data/index.js
+++ b/api-docs/scripts/dist/openapi-paths-to-hugo-data/index.js
@@ -1,4 +1,4 @@
-'use strict';
+"use strict";
/**
* OpenAPI to Hugo Data Converter
*
@@ -7,68 +7,45 @@
*
* @module openapi-paths-to-hugo-data
*/
-var __createBinding =
- (this && this.__createBinding) ||
- (Object.create
- ? function (o, m, k, k2) {
- if (k2 === undefined) k2 = k;
- var desc = Object.getOwnPropertyDescriptor(m, k);
- if (
- !desc ||
- ('get' in desc ? !m.__esModule : desc.writable || desc.configurable)
- ) {
- desc = {
- enumerable: true,
- get: function () {
- return m[k];
- },
- };
- }
- Object.defineProperty(o, k2, desc);
- }
- : function (o, m, k, k2) {
- if (k2 === undefined) k2 = k;
- o[k2] = m[k];
- });
-var __setModuleDefault =
- (this && this.__setModuleDefault) ||
- (Object.create
- ? function (o, v) {
- Object.defineProperty(o, 'default', { enumerable: true, value: v });
- }
- : function (o, v) {
- o['default'] = v;
- });
-var __importStar =
- (this && this.__importStar) ||
- (function () {
- var ownKeys = function (o) {
- ownKeys =
- Object.getOwnPropertyNames ||
- function (o) {
- var ar = [];
- for (var k in o)
- if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
- return ar;
+var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ var desc = Object.getOwnPropertyDescriptor(m, k);
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
+ desc = { enumerable: true, get: function() { return m[k]; } };
+ }
+ Object.defineProperty(o, k2, desc);
+}) : (function(o, m, k, k2) {
+ if (k2 === undefined) k2 = k;
+ o[k2] = m[k];
+}));
+var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
+}) : function(o, v) {
+ o["default"] = v;
+});
+var __importStar = (this && this.__importStar) || (function () {
+ var ownKeys = function(o) {
+ ownKeys = Object.getOwnPropertyNames || function (o) {
+ var ar = [];
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
+ return ar;
};
- return ownKeys(o);
+ return ownKeys(o);
};
return function (mod) {
- if (mod && mod.__esModule) return mod;
- var result = {};
- if (mod != null)
- for (var k = ownKeys(mod), i = 0; i < k.length; i++)
- if (k[i] !== 'default') __createBinding(result, mod, k[i]);
- __setModuleDefault(result, mod);
- return result;
+ if (mod && mod.__esModule) return mod;
+ var result = {};
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
+ __setModuleDefault(result, mod);
+ return result;
};
- })();
-Object.defineProperty(exports, '__esModule', { value: true });
+})();
+Object.defineProperty(exports, "__esModule", { value: true });
exports.generateHugoDataByTag = generateHugoDataByTag;
exports.generateHugoData = generateHugoData;
-const yaml = __importStar(require('js-yaml'));
-const fs = __importStar(require('fs'));
-const path = __importStar(require('path'));
+const yaml = __importStar(require("js-yaml"));
+const fs = __importStar(require("fs"));
+const path = __importStar(require("path"));
/**
* Read a YAML file and parse it
*
@@ -77,8 +54,8 @@ const path = __importStar(require('path'));
* @returns Parsed YAML content
*/
function readFile(filepath, encoding = 'utf8') {
- const content = fs.readFileSync(filepath, encoding);
- return yaml.load(content);
+ const content = fs.readFileSync(filepath, encoding);
+ return yaml.load(content);
}
/**
* Write data to a YAML file
@@ -87,7 +64,7 @@ function readFile(filepath, encoding = 'utf8') {
* @param outputTo - Output file path
*/
function writeDataFile(data, outputTo) {
- fs.writeFileSync(outputTo, yaml.dump(data));
+ fs.writeFileSync(outputTo, yaml.dump(data));
}
/**
* Write data to a JSON file
@@ -96,22 +73,22 @@ function writeDataFile(data, outputTo) {
* @param outputTo - Output file path
*/
function writeJsonFile(data, outputTo) {
- fs.writeFileSync(outputTo, JSON.stringify(data, null, 2));
+ fs.writeFileSync(outputTo, JSON.stringify(data, null, 2));
}
/**
* OpenAPI utility functions
*/
const openapiUtils = {
- /**
- * Check if a path fragment is a placeholder (e.g., {id})
- *
- * @param str - Path fragment to check
- * @returns True if the fragment is a placeholder
- */
- isPlaceholderFragment(str) {
- const placeholderRegex = /^\{.*\}$/;
- return placeholderRegex.test(str);
- },
+ /**
+ * Check if a path fragment is a placeholder (e.g., {id})
+ *
+ * @param str - Path fragment to check
+ * @returns True if the fragment is a placeholder
+ */
+ isPlaceholderFragment(str) {
+ const placeholderRegex = /^\{.*\}$/;
+ return placeholderRegex.test(str);
+ },
};
/**
* Convert tag name to URL-friendly slug
@@ -120,35 +97,35 @@ const openapiUtils = {
* @returns URL-friendly slug (e.g., "write-data", "processing-engine")
*/
function slugifyTag(tagName) {
- return tagName
- .toLowerCase()
- .replace(/[^a-z0-9]+/g, '-')
- .replace(/^-|-$/g, '');
+ return tagName
+ .toLowerCase()
+ .replace(/[^a-z0-9]+/g, '-')
+ .replace(/^-|-$/g, '');
}
/**
* Menu group mappings for tag-based navigation
* Maps OpenAPI tags to sidebar groups
*/
const TAG_MENU_GROUPS = {
- // Concepts group
- 'Quick start': 'Concepts',
- Authentication: 'Concepts',
- 'Headers and parameters': 'Concepts',
- 'Response codes': 'Concepts',
- // Data Operations group
- 'Write data': 'Data Operations',
- 'Query data': 'Data Operations',
- 'Cache data': 'Data Operations',
- // Administration group
- Database: 'Administration',
- Table: 'Administration',
- Token: 'Administration',
- // Processing Engine group
- 'Processing engine': 'Processing Engine',
- // Server group
- 'Server information': 'Server',
- // Compatibility group
- 'Compatibility endpoints': 'Compatibility',
+ // Concepts group
+ 'Quick start': 'Concepts',
+ Authentication: 'Concepts',
+ 'Headers and parameters': 'Concepts',
+ 'Response codes': 'Concepts',
+ // Data Operations group
+ 'Write data': 'Data Operations',
+ 'Query data': 'Data Operations',
+ 'Cache data': 'Data Operations',
+ // Administration group
+ Database: 'Administration',
+ Table: 'Administration',
+ Token: 'Administration',
+ // Processing Engine group
+ 'Processing engine': 'Processing Engine',
+ // Server group
+ 'Server information': 'Server',
+ // Compatibility group
+ 'Compatibility endpoints': 'Compatibility',
};
/**
* Get menu group for a tag
@@ -157,20 +134,20 @@ const TAG_MENU_GROUPS = {
* @returns Menu group name or 'Other' if not mapped
*/
function getMenuGroupForTag(tagName) {
- return TAG_MENU_GROUPS[tagName] || 'Other';
+ return TAG_MENU_GROUPS[tagName] || 'Other';
}
/**
* HTTP methods to check for operations
*/
const HTTP_METHODS = [
- 'get',
- 'post',
- 'put',
- 'patch',
- 'delete',
- 'options',
- 'head',
- 'trace',
+ 'get',
+ 'post',
+ 'put',
+ 'patch',
+ 'delete',
+ 'options',
+ 'head',
+ 'trace',
];
/**
* Extract all operations from an OpenAPI document grouped by tag
@@ -179,40 +156,40 @@ const HTTP_METHODS = [
* @returns Map of tag name to operations with that tag
*/
function extractOperationsByTag(openapi) {
- const tagOperations = new Map();
- Object.entries(openapi.paths).forEach(([pathKey, pathItem]) => {
- HTTP_METHODS.forEach((method) => {
- const operation = pathItem[method];
- if (operation) {
- const opMeta = {
- operationId: operation.operationId || `${method}-${pathKey}`,
- method: method.toUpperCase(),
- path: pathKey,
- summary: operation.summary || '',
- tags: operation.tags || [],
- };
- // Extract compatibility version if present
- if (operation['x-compatibility-version']) {
- opMeta.compatVersion = operation['x-compatibility-version'];
- }
- // Extract externalDocs if present
- if (operation.externalDocs) {
- opMeta.externalDocs = {
- description: operation.externalDocs.description || '',
- url: operation.externalDocs.url,
- };
- }
- // Add operation to each of its tags
- (operation.tags || []).forEach((tag) => {
- if (!tagOperations.has(tag)) {
- tagOperations.set(tag, []);
- }
- tagOperations.get(tag).push(opMeta);
+ const tagOperations = new Map();
+ Object.entries(openapi.paths).forEach(([pathKey, pathItem]) => {
+ HTTP_METHODS.forEach((method) => {
+ const operation = pathItem[method];
+ if (operation) {
+ const opMeta = {
+ operationId: operation.operationId || `${method}-${pathKey}`,
+ method: method.toUpperCase(),
+ path: pathKey,
+ summary: operation.summary || '',
+ tags: operation.tags || [],
+ };
+ // Extract compatibility version if present
+ if (operation['x-compatibility-version']) {
+ opMeta.compatVersion = operation['x-compatibility-version'];
+ }
+ // Extract externalDocs if present
+ if (operation.externalDocs) {
+ opMeta.externalDocs = {
+ description: operation.externalDocs.description || '',
+ url: operation.externalDocs.url,
+ };
+ }
+ // Add operation to each of its tags
+ (operation.tags || []).forEach((tag) => {
+ if (!tagOperations.has(tag)) {
+ tagOperations.set(tag, []);
+ }
+ tagOperations.get(tag).push(opMeta);
+ });
+ }
});
- }
});
- });
- return tagOperations;
+ return tagOperations;
}
/**
* Write OpenAPI specs grouped by tag to separate files
@@ -223,81 +200,83 @@ function extractOperationsByTag(openapi) {
* @param outPath - Output directory path
*/
function writeTagOpenapis(openapi, prefix, outPath) {
- const tagOperations = extractOperationsByTag(openapi);
- // Process each tag
- tagOperations.forEach((operations, tagName) => {
- // Deep copy openapi
- const doc = JSON.parse(JSON.stringify(openapi));
- // Filter paths to only include those with operations for this tag
- const filteredPaths = {};
- Object.entries(openapi.paths).forEach(([pathKey, pathItem]) => {
- const filteredPathItem = {};
- let hasOperations = false;
- HTTP_METHODS.forEach((method) => {
- const operation = pathItem[method];
- if (operation?.tags?.includes(tagName)) {
- filteredPathItem[method] = operation;
- hasOperations = true;
+ const tagOperations = extractOperationsByTag(openapi);
+ // Process each tag
+ tagOperations.forEach((operations, tagName) => {
+ // Deep copy openapi
+ const doc = JSON.parse(JSON.stringify(openapi));
+ // Filter paths to only include those with operations for this tag
+ const filteredPaths = {};
+ Object.entries(openapi.paths).forEach(([pathKey, pathItem]) => {
+ const filteredPathItem = {};
+ let hasOperations = false;
+ HTTP_METHODS.forEach((method) => {
+ const operation = pathItem[method];
+ if (operation?.tags?.includes(tagName)) {
+ // Clone the operation and restrict tags to only this tag
+ // This prevents RapiDoc from rendering the operation multiple times
+ // (once per tag) when an operation belongs to multiple tags
+ const filteredOperation = { ...operation, tags: [tagName] };
+ filteredPathItem[method] = filteredOperation;
+ hasOperations = true;
+ }
+ });
+ // Include path-level parameters if we have operations
+ if (hasOperations) {
+ if (pathItem.parameters) {
+ filteredPathItem.parameters = pathItem.parameters;
+ }
+ filteredPaths[pathKey] = filteredPathItem;
+ }
+ });
+ doc.paths = filteredPaths;
+ // Filter tags to only include this tag (and trait tags for context)
+ if (doc.tags) {
+ doc.tags = doc.tags.filter((tag) => tag.name === tagName || tag['x-traitTag']);
}
- });
- // Include path-level parameters if we have operations
- if (hasOperations) {
- if (pathItem.parameters) {
- filteredPathItem.parameters = pathItem.parameters;
+ // Update info
+ const tagSlug = slugifyTag(tagName);
+ doc.info.title = tagName;
+ doc.info.description = `API reference for ${tagName}`;
+ doc['x-tagGroup'] = tagName;
+ try {
+ if (!fs.existsSync(outPath)) {
+ fs.mkdirSync(outPath, { recursive: true });
+ }
+ const baseFilename = `${prefix}${tagSlug}`;
+ const yamlPath = path.resolve(outPath, `${baseFilename}.yaml`);
+ const jsonPath = path.resolve(outPath, `${baseFilename}.json`);
+ writeDataFile(doc, yamlPath);
+ writeJsonFile(doc, jsonPath);
+ console.log(`Generated tag spec: ${baseFilename}.yaml (${Object.keys(filteredPaths).length} paths, ${operations.length} operations)`);
+ }
+ catch (err) {
+ console.error(`Error writing tag group ${tagName}:`, err);
+ }
+ });
+ // Also create specs for conceptual tags (x-traitTag) without operations
+ (openapi.tags || []).forEach((tag) => {
+ if (tag['x-traitTag'] && !tagOperations.has(tag.name)) {
+ const doc = JSON.parse(JSON.stringify(openapi));
+ doc.paths = {};
+ doc.tags = [tag];
+ doc.info.title = tag.name;
+ doc.info.description = tag.description || `API reference for ${tag.name}`;
+ doc['x-tagGroup'] = tag.name;
+ const tagSlug = slugifyTag(tag.name);
+ try {
+ const baseFilename = `${prefix}${tagSlug}`;
+ const yamlPath = path.resolve(outPath, `${baseFilename}.yaml`);
+ const jsonPath = path.resolve(outPath, `${baseFilename}.json`);
+ writeDataFile(doc, yamlPath);
+ writeJsonFile(doc, jsonPath);
+ console.log(`Generated conceptual tag spec: ${baseFilename}.yaml`);
+ }
+ catch (err) {
+ console.error(`Error writing conceptual tag ${tag.name}:`, err);
+ }
}
- filteredPaths[pathKey] = filteredPathItem;
- }
});
- doc.paths = filteredPaths;
- // Filter tags to only include this tag (and trait tags for context)
- if (doc.tags) {
- doc.tags = doc.tags.filter(
- (tag) => tag.name === tagName || tag['x-traitTag']
- );
- }
- // Update info
- const tagSlug = slugifyTag(tagName);
- doc.info.title = tagName;
- doc.info.description = `API reference for ${tagName}`;
- doc['x-tagGroup'] = tagName;
- try {
- if (!fs.existsSync(outPath)) {
- fs.mkdirSync(outPath, { recursive: true });
- }
- const baseFilename = `${prefix}${tagSlug}`;
- const yamlPath = path.resolve(outPath, `${baseFilename}.yaml`);
- const jsonPath = path.resolve(outPath, `${baseFilename}.json`);
- writeDataFile(doc, yamlPath);
- writeJsonFile(doc, jsonPath);
- console.log(
- `Generated tag spec: ${baseFilename}.yaml (${Object.keys(filteredPaths).length} paths, ${operations.length} operations)`
- );
- } catch (err) {
- console.error(`Error writing tag group ${tagName}:`, err);
- }
- });
- // Also create specs for conceptual tags (x-traitTag) without operations
- (openapi.tags || []).forEach((tag) => {
- if (tag['x-traitTag'] && !tagOperations.has(tag.name)) {
- const doc = JSON.parse(JSON.stringify(openapi));
- doc.paths = {};
- doc.tags = [tag];
- doc.info.title = tag.name;
- doc.info.description = tag.description || `API reference for ${tag.name}`;
- doc['x-tagGroup'] = tag.name;
- const tagSlug = slugifyTag(tag.name);
- try {
- const baseFilename = `${prefix}${tagSlug}`;
- const yamlPath = path.resolve(outPath, `${baseFilename}.yaml`);
- const jsonPath = path.resolve(outPath, `${baseFilename}.json`);
- writeDataFile(doc, yamlPath);
- writeJsonFile(doc, jsonPath);
- console.log(`Generated conceptual tag spec: ${baseFilename}.yaml`);
- } catch (err) {
- console.error(`Error writing conceptual tag ${tag.name}:`, err);
- }
- }
- });
}
/**
* Write OpenAPI specs grouped by path to separate files
@@ -308,80 +287,79 @@ function writeTagOpenapis(openapi, prefix, outPath) {
* @param outPath - Output directory path
*/
function writePathOpenapis(openapi, prefix, outPath) {
- const pathGroups = {};
- // Group paths by their base path (first 3-4 segments, excluding placeholders)
- Object.keys(openapi.paths)
- .sort()
- .forEach((p) => {
- const delimiter = '/';
- let key = p.split(delimiter);
- // Check if this is an item path (ends with a placeholder)
- let isItemPath = openapiUtils.isPlaceholderFragment(key[key.length - 1]);
- if (isItemPath) {
- key = key.slice(0, -1);
- }
- // Take first 4 segments
- key = key.slice(0, 4);
- // Check if the last segment is still a placeholder
- isItemPath = openapiUtils.isPlaceholderFragment(key[key.length - 1]);
- if (isItemPath) {
- key = key.slice(0, -1);
- }
- const groupKey = key.join('/');
- pathGroups[groupKey] = pathGroups[groupKey] || {};
- pathGroups[groupKey][p] = openapi.paths[p];
- });
- // Write each path group to separate YAML and JSON files
- Object.keys(pathGroups).forEach((pg) => {
- // Deep copy openapi
- const doc = JSON.parse(JSON.stringify(openapi));
- doc.paths = pathGroups[pg];
- // Collect tags used by operations in this path group
- const usedTags = new Set();
- Object.values(doc.paths).forEach((pathItem) => {
- const httpMethods = [
- 'get',
- 'post',
- 'put',
- 'patch',
- 'delete',
- 'options',
- 'head',
- 'trace',
- ];
- httpMethods.forEach((method) => {
- const operation = pathItem[method];
- if (operation?.tags) {
- operation.tags.forEach((tag) => usedTags.add(tag));
+ const pathGroups = {};
+ // Group paths by their base path (first 3-4 segments, excluding placeholders)
+ Object.keys(openapi.paths)
+ .sort()
+ .forEach((p) => {
+ const delimiter = '/';
+ let key = p.split(delimiter);
+ // Check if this is an item path (ends with a placeholder)
+ let isItemPath = openapiUtils.isPlaceholderFragment(key[key.length - 1]);
+ if (isItemPath) {
+ key = key.slice(0, -1);
+ }
+ // Take first 4 segments
+ key = key.slice(0, 4);
+ // Check if the last segment is still a placeholder
+ isItemPath = openapiUtils.isPlaceholderFragment(key[key.length - 1]);
+ if (isItemPath) {
+ key = key.slice(0, -1);
+ }
+ const groupKey = key.join('/');
+ pathGroups[groupKey] = pathGroups[groupKey] || {};
+ pathGroups[groupKey][p] = openapi.paths[p];
+ });
+ // Write each path group to separate YAML and JSON files
+ Object.keys(pathGroups).forEach((pg) => {
+ // Deep copy openapi
+ const doc = JSON.parse(JSON.stringify(openapi));
+ doc.paths = pathGroups[pg];
+ // Collect tags used by operations in this path group
+ const usedTags = new Set();
+ Object.values(doc.paths).forEach((pathItem) => {
+ const httpMethods = [
+ 'get',
+ 'post',
+ 'put',
+ 'patch',
+ 'delete',
+ 'options',
+ 'head',
+ 'trace',
+ ];
+ httpMethods.forEach((method) => {
+ const operation = pathItem[method];
+ if (operation?.tags) {
+ operation.tags.forEach((tag) => usedTags.add(tag));
+ }
+ });
+ });
+ // Filter tags to only include those used by operations in this path group
+ // Exclude x-traitTag tags (supplementary documentation tags)
+ if (doc.tags) {
+ doc.tags = doc.tags.filter((tag) => usedTags.has(tag.name) && !tag['x-traitTag']);
+ }
+ // Simplify info for path-specific docs
+ doc.info.title = pg;
+ doc.info.description = `API reference for ${pg}`;
+ doc['x-pathGroup'] = pg;
+ try {
+ if (!fs.existsSync(outPath)) {
+ fs.mkdirSync(outPath, { recursive: true });
+ }
+ const baseFilename = `${prefix}${pg.replaceAll('/', '-').replace(/^-/, '')}`;
+ const yamlPath = path.resolve(outPath, `${baseFilename}.yaml`);
+ const jsonPath = path.resolve(outPath, `${baseFilename}.json`);
+ // Write both YAML and JSON versions
+ writeDataFile(doc, yamlPath);
+ writeJsonFile(doc, jsonPath);
+ console.log(`Generated: ${baseFilename}.yaml and ${baseFilename}.json`);
+ }
+ catch (err) {
+ console.error(`Error writing path group ${pg}:`, err);
}
- });
});
- // Filter tags to only include those used by operations in this path group
- // Exclude x-traitTag tags (supplementary documentation tags)
- if (doc.tags) {
- doc.tags = doc.tags.filter(
- (tag) => usedTags.has(tag.name) && !tag['x-traitTag']
- );
- }
- // Simplify info for path-specific docs
- doc.info.title = pg;
- doc.info.description = `API reference for ${pg}`;
- doc['x-pathGroup'] = pg;
- try {
- if (!fs.existsSync(outPath)) {
- fs.mkdirSync(outPath, { recursive: true });
- }
- const baseFilename = `${prefix}${pg.replaceAll('/', '-').replace(/^-/, '')}`;
- const yamlPath = path.resolve(outPath, `${baseFilename}.yaml`);
- const jsonPath = path.resolve(outPath, `${baseFilename}.json`);
- // Write both YAML and JSON versions
- writeDataFile(doc, yamlPath);
- writeJsonFile(doc, jsonPath);
- console.log(`Generated: ${baseFilename}.yaml and ${baseFilename}.json`);
- } catch (err) {
- console.error(`Error writing path group ${pg}:`, err);
- }
- });
}
/**
* Create article metadata for a path group
@@ -390,119 +368,107 @@ function writePathOpenapis(openapi, prefix, outPath) {
* @returns Article metadata object
*/
function createArticleDataForPathGroup(openapi) {
- const article = {
- path: '',
- fields: {
- name: openapi['x-pathGroup'] || '',
- describes: Object.keys(openapi.paths),
- },
- };
- /**
- * Convert OpenAPI path to Hugo-friendly article path
- * Legacy endpoints (without /api/ prefix) go under api/ directly
- * Versioned endpoints (with /api/vN/) keep their structure
- *
- * @param p - Path to convert (e.g., '/health', '/api/v3/query_sql')
- * @returns Path suitable for Hugo content directory (e.g., 'api/health', 'api/v3/query_sql')
- */
- const toHugoPath = (p) => {
- if (!p) {
- return '';
- }
- // If path doesn't start with /api/, it's a legacy endpoint
- // Place it directly under api/ to avoid collision with /api/v1/* paths
- if (!p.startsWith('/api/')) {
- // /health -> api/health
- // /write -> api/write
- return `api${p}`;
- }
- // /api/v1/health -> api/v1/health
- // /api/v2/write -> api/v2/write
- // /api/v3/query_sql -> api/v3/query_sql
- return p.replace(/^\//, '');
- };
- /**
- * Convert path to tag-friendly format (dashes instead of slashes)
- *
- * @param p - Path to convert
- * @returns Tag-friendly path
- */
- const toTagPath = (p) => {
- if (!p) {
- return '';
- }
- return p.replace(/^\//, '').replaceAll('/', '-');
- };
- const pathGroup = openapi['x-pathGroup'] || '';
- article.path = toHugoPath(pathGroup);
- // Store original path for menu display (shows actual endpoint path)
- article.fields.menuName = pathGroup;
- article.fields.title = openapi.info?.title;
- article.fields.description = openapi.description;
- const pathGroupFrags = path.parse(openapi['x-pathGroup'] || '');
- article.fields.tags = [pathGroupFrags?.dir, pathGroupFrags?.name]
- .filter(Boolean)
- .map((t) => toTagPath(t));
- // Extract x-relatedLinks and OpenAPI tags from path items or operations
- const relatedLinks = [];
- const apiTags = [];
- const httpMethods = [
- 'get',
- 'post',
- 'put',
- 'patch',
- 'delete',
- 'options',
- 'head',
- 'trace',
- ];
- Object.values(openapi.paths).forEach((pathItem) => {
- // Check path-level x-relatedLinks
- if (
- pathItem['x-relatedLinks'] &&
- Array.isArray(pathItem['x-relatedLinks'])
- ) {
- relatedLinks.push(
- ...pathItem['x-relatedLinks'].filter(
- (link) => !relatedLinks.includes(link)
- )
- );
- }
- // Check operation-level x-relatedLinks and tags
- httpMethods.forEach((method) => {
- const operation = pathItem[method];
- if (operation) {
- // Extract x-relatedLinks
- if (
- operation['x-relatedLinks'] &&
- Array.isArray(operation['x-relatedLinks'])
- ) {
- relatedLinks.push(
- ...operation['x-relatedLinks'].filter(
- (link) => !relatedLinks.includes(link)
- )
- );
+ const article = {
+ path: '',
+ fields: {
+ name: openapi['x-pathGroup'] || '',
+ describes: Object.keys(openapi.paths),
+ },
+ };
+ /**
+ * Convert OpenAPI path to Hugo-friendly article path
+ * Legacy endpoints (without /api/ prefix) go under api/ directly
+ * Versioned endpoints (with /api/vN/) keep their structure
+ *
+ * @param p - Path to convert (e.g., '/health', '/api/v3/query_sql')
+ * @returns Path suitable for Hugo content directory (e.g., 'api/health', 'api/v3/query_sql')
+ */
+ const toHugoPath = (p) => {
+ if (!p) {
+ return '';
}
- // Extract OpenAPI tags from operation
- if (operation.tags && Array.isArray(operation.tags)) {
- operation.tags.forEach((tag) => {
- if (!apiTags.includes(tag)) {
- apiTags.push(tag);
+ // If path doesn't start with /api/, it's a legacy endpoint
+ // Place it directly under api/ to avoid collision with /api/v1/* paths
+ if (!p.startsWith('/api/')) {
+ // /health -> api/health
+ // /write -> api/write
+ return `api${p}`;
+ }
+ // /api/v1/health -> api/v1/health
+ // /api/v2/write -> api/v2/write
+ // /api/v3/query_sql -> api/v3/query_sql
+ return p.replace(/^\//, '');
+ };
+ /**
+ * Convert path to tag-friendly format (dashes instead of slashes)
+ *
+ * @param p - Path to convert
+ * @returns Tag-friendly path
+ */
+ const toTagPath = (p) => {
+ if (!p) {
+ return '';
+ }
+ return p.replace(/^\//, '').replaceAll('/', '-');
+ };
+ const pathGroup = openapi['x-pathGroup'] || '';
+ article.path = toHugoPath(pathGroup);
+ // Store original path for menu display (shows actual endpoint path)
+ article.fields.menuName = pathGroup;
+ article.fields.title = openapi.info?.title;
+ article.fields.description = openapi.description;
+ const pathGroupFrags = path.parse(openapi['x-pathGroup'] || '');
+ article.fields.tags = [pathGroupFrags?.dir, pathGroupFrags?.name]
+ .filter(Boolean)
+ .map((t) => toTagPath(t));
+ // Extract x-relatedLinks and OpenAPI tags from path items or operations
+ const relatedLinks = [];
+ const apiTags = [];
+ const httpMethods = [
+ 'get',
+ 'post',
+ 'put',
+ 'patch',
+ 'delete',
+ 'options',
+ 'head',
+ 'trace',
+ ];
+ Object.values(openapi.paths).forEach((pathItem) => {
+ // Check path-level x-relatedLinks
+ if (pathItem['x-relatedLinks'] &&
+ Array.isArray(pathItem['x-relatedLinks'])) {
+ relatedLinks.push(...pathItem['x-relatedLinks'].filter((link) => !relatedLinks.includes(link)));
+ }
+ // Check operation-level x-relatedLinks and tags
+ httpMethods.forEach((method) => {
+ const operation = pathItem[method];
+ if (operation) {
+ // Extract x-relatedLinks
+ if (operation['x-relatedLinks'] &&
+ Array.isArray(operation['x-relatedLinks'])) {
+ relatedLinks.push(...operation['x-relatedLinks'].filter((link) => !relatedLinks.includes(link)));
+ }
+ // Extract OpenAPI tags from operation
+ if (operation.tags && Array.isArray(operation.tags)) {
+ operation.tags.forEach((tag) => {
+ if (!apiTags.includes(tag)) {
+ apiTags.push(tag);
+ }
+ });
+ }
}
- });
- }
- }
+ });
});
- });
- // Only add related if there are links
- if (relatedLinks.length > 0) {
- article.fields.related = relatedLinks;
- }
- // Add OpenAPI tags from operations (for Hugo frontmatter)
- if (apiTags.length > 0) {
- article.fields.apiTags = apiTags;
- }
- return article;
+ // Only add related if there are links
+ if (relatedLinks.length > 0) {
+ article.fields.related = relatedLinks;
+ }
+ // Add OpenAPI tags from operations (for Hugo frontmatter)
+ if (apiTags.length > 0) {
+ article.fields.apiTags = apiTags;
+ }
+ return article;
}
/**
* Write OpenAPI article metadata to Hugo data files
@@ -513,50 +479,49 @@ function createArticleDataForPathGroup(openapi) {
* @param opts - Options including file pattern filter
*/
function writeOpenapiArticleData(sourcePath, targetPath, opts) {
- /**
- * Check if path is a file
- */
- const isFile = (filePath) => {
- return fs.lstatSync(filePath).isFile();
- };
- /**
- * Check if filename matches pattern
- */
- const matchesPattern = (filePath) => {
- return opts.filePattern
- ? path.parse(filePath).name.startsWith(opts.filePattern)
- : true;
- };
- try {
- const articles = fs
- .readdirSync(sourcePath)
- .map((fileName) => path.join(sourcePath, fileName))
- .filter(matchesPattern)
- .filter(isFile)
- .filter(
- (filePath) => filePath.endsWith('.yaml') || filePath.endsWith('.yml')
- ) // Only process YAML files
- .map((filePath) => {
- const openapi = readFile(filePath);
- const article = createArticleDataForPathGroup(openapi);
- article.fields.source = filePath;
- // Hugo omits "/static" from the URI when serving files stored in "./static"
- article.fields.staticFilePath = filePath.replace(/^static\//, '/');
- return article;
- });
- if (!fs.existsSync(targetPath)) {
- fs.mkdirSync(targetPath, { recursive: true });
+ /**
+ * Check if path is a file
+ */
+ const isFile = (filePath) => {
+ return fs.lstatSync(filePath).isFile();
+ };
+ /**
+ * Check if filename matches pattern
+ */
+ const matchesPattern = (filePath) => {
+ return opts.filePattern
+ ? path.parse(filePath).name.startsWith(opts.filePattern)
+ : true;
+ };
+ try {
+ const articles = fs
+ .readdirSync(sourcePath)
+ .map((fileName) => path.join(sourcePath, fileName))
+ .filter(matchesPattern)
+ .filter(isFile)
+ .filter((filePath) => filePath.endsWith('.yaml') || filePath.endsWith('.yml')) // Only process YAML files
+ .map((filePath) => {
+ const openapi = readFile(filePath);
+ const article = createArticleDataForPathGroup(openapi);
+ article.fields.source = filePath;
+ // Hugo omits "/static" from the URI when serving files stored in "./static"
+ article.fields.staticFilePath = filePath.replace(/^static\//, '/');
+ return article;
+ });
+ if (!fs.existsSync(targetPath)) {
+ fs.mkdirSync(targetPath, { recursive: true });
+ }
+ const articleCollection = { articles };
+ // Write both YAML and JSON versions
+ const yamlPath = path.resolve(targetPath, 'articles.yml');
+ const jsonPath = path.resolve(targetPath, 'articles.json');
+ writeDataFile(articleCollection, yamlPath);
+ writeJsonFile(articleCollection, jsonPath);
+ console.log(`Generated ${articles.length} articles in ${targetPath}`);
+ }
+ catch (e) {
+ console.error('Error writing article data:', e);
}
- const articleCollection = { articles };
- // Write both YAML and JSON versions
- const yamlPath = path.resolve(targetPath, 'articles.yml');
- const jsonPath = path.resolve(targetPath, 'articles.json');
- writeDataFile(articleCollection, yamlPath);
- writeJsonFile(articleCollection, jsonPath);
- console.log(`Generated ${articles.length} articles in ${targetPath}`);
- } catch (e) {
- console.error('Error writing article data:', e);
- }
}
/**
* Create article data for a tag-based grouping
@@ -567,54 +532,53 @@ function writeOpenapiArticleData(sourcePath, targetPath, opts) {
* @returns Article metadata object
*/
function createArticleDataForTag(openapi, operations, tagMeta) {
- const tagName = openapi['x-tagGroup'] || '';
- const tagSlug = slugifyTag(tagName);
- const isConceptual = tagMeta?.['x-traitTag'] === true;
- const article = {
- path: `api/${tagSlug}`,
- fields: {
- name: tagName,
- describes: Object.keys(openapi.paths),
- title: tagName,
- description:
- tagMeta?.description ||
- openapi.info?.description ||
- `API reference for ${tagName}`,
- tag: tagName,
- isConceptual,
- menuGroup: getMenuGroupForTag(tagName),
- operations: operations.map((op) => ({
- operationId: op.operationId,
- method: op.method,
- path: op.path,
- summary: op.summary,
- tags: op.tags,
- ...(op.compatVersion && { compatVersion: op.compatVersion }),
- ...(op.externalDocs && { externalDocs: op.externalDocs }),
- })),
- },
- };
- // Add tag description for conceptual pages
- if (tagMeta?.description) {
- article.fields.tagDescription = tagMeta.description;
- }
- // Aggregate unique externalDocs URLs from operations into article-level related
- // This populates Hugo frontmatter `related` field for "Related content" links
- const relatedUrls = new Set();
- // First check tag-level externalDocs
- if (tagMeta?.externalDocs?.url) {
- relatedUrls.add(tagMeta.externalDocs.url);
- }
- // Then aggregate from operations
- operations.forEach((op) => {
- if (op.externalDocs?.url) {
- relatedUrls.add(op.externalDocs.url);
+ const tagName = openapi['x-tagGroup'] || '';
+ const tagSlug = slugifyTag(tagName);
+ const isConceptual = tagMeta?.['x-traitTag'] === true;
+ const article = {
+ path: `api/${tagSlug}`,
+ fields: {
+ name: tagName,
+ describes: Object.keys(openapi.paths),
+ title: tagName,
+ description: tagMeta?.description ||
+ openapi.info?.description ||
+ `API reference for ${tagName}`,
+ tag: tagName,
+ isConceptual,
+ menuGroup: getMenuGroupForTag(tagName),
+ operations: operations.map((op) => ({
+ operationId: op.operationId,
+ method: op.method,
+ path: op.path,
+ summary: op.summary,
+ tags: op.tags,
+ ...(op.compatVersion && { compatVersion: op.compatVersion }),
+ ...(op.externalDocs && { externalDocs: op.externalDocs }),
+ })),
+ },
+ };
+ // Add tag description for conceptual pages
+ if (tagMeta?.description) {
+ article.fields.tagDescription = tagMeta.description;
}
- });
- if (relatedUrls.size > 0) {
- article.fields.related = Array.from(relatedUrls);
- }
- return article;
+ // Aggregate unique externalDocs URLs from operations into article-level related
+ // This populates Hugo frontmatter `related` field for "Related content" links
+ const relatedUrls = new Set();
+ // First check tag-level externalDocs
+ if (tagMeta?.externalDocs?.url) {
+ relatedUrls.add(tagMeta.externalDocs.url);
+ }
+ // Then aggregate from operations
+ operations.forEach((op) => {
+ if (op.externalDocs?.url) {
+ relatedUrls.add(op.externalDocs.url);
+ }
+ });
+ if (relatedUrls.size > 0) {
+ article.fields.related = Array.from(relatedUrls);
+ }
+ return article;
}
/**
* Write tag-based OpenAPI article metadata to Hugo data files
@@ -626,85 +590,77 @@ function createArticleDataForTag(openapi, operations, tagMeta) {
* @param opts - Options including file pattern filter
*/
function writeOpenapiTagArticleData(sourcePath, targetPath, openapi, opts) {
- const isFile = (filePath) => {
- return fs.lstatSync(filePath).isFile();
- };
- const matchesPattern = (filePath) => {
- return opts.filePattern
- ? path.parse(filePath).name.startsWith(opts.filePattern)
- : true;
- };
- // Create tag metadata lookup
- const tagMetaMap = new Map();
- (openapi.tags || []).forEach((tag) => {
- tagMetaMap.set(tag.name, tag);
- });
- try {
- const articles = fs
- .readdirSync(sourcePath)
- .map((fileName) => path.join(sourcePath, fileName))
- .filter(matchesPattern)
- .filter(isFile)
- .filter(
- (filePath) => filePath.endsWith('.yaml') || filePath.endsWith('.yml')
- )
- .map((filePath) => {
- const tagOpenapi = readFile(filePath);
- const tagName =
- tagOpenapi['x-tagGroup'] || tagOpenapi.info?.title || '';
- const tagMeta = tagMetaMap.get(tagName);
- // Extract operations from the tag-filtered spec
- const operations = [];
- Object.entries(tagOpenapi.paths).forEach(([pathKey, pathItem]) => {
- HTTP_METHODS.forEach((method) => {
- const operation = pathItem[method];
- if (operation) {
- const opMeta = {
- operationId: operation.operationId || `${method}-${pathKey}`,
- method: method.toUpperCase(),
- path: pathKey,
- summary: operation.summary || '',
- tags: operation.tags || [],
- };
- // Extract compatibility version if present
- if (operation['x-compatibility-version']) {
- opMeta.compatVersion = operation['x-compatibility-version'];
- }
- // Extract externalDocs if present
- if (operation.externalDocs) {
- opMeta.externalDocs = {
- description: operation.externalDocs.description || '',
- url: operation.externalDocs.url,
- };
- }
- operations.push(opMeta);
- }
- });
+ const isFile = (filePath) => {
+ return fs.lstatSync(filePath).isFile();
+ };
+ const matchesPattern = (filePath) => {
+ return opts.filePattern
+ ? path.parse(filePath).name.startsWith(opts.filePattern)
+ : true;
+ };
+ // Create tag metadata lookup
+ const tagMetaMap = new Map();
+ (openapi.tags || []).forEach((tag) => {
+ tagMetaMap.set(tag.name, tag);
+ });
+ try {
+ const articles = fs
+ .readdirSync(sourcePath)
+ .map((fileName) => path.join(sourcePath, fileName))
+ .filter(matchesPattern)
+ .filter(isFile)
+ .filter((filePath) => filePath.endsWith('.yaml') || filePath.endsWith('.yml'))
+ .map((filePath) => {
+ const tagOpenapi = readFile(filePath);
+ const tagName = tagOpenapi['x-tagGroup'] || tagOpenapi.info?.title || '';
+ const tagMeta = tagMetaMap.get(tagName);
+ // Extract operations from the tag-filtered spec
+ const operations = [];
+ Object.entries(tagOpenapi.paths).forEach(([pathKey, pathItem]) => {
+ HTTP_METHODS.forEach((method) => {
+ const operation = pathItem[method];
+ if (operation) {
+ const opMeta = {
+ operationId: operation.operationId || `${method}-${pathKey}`,
+ method: method.toUpperCase(),
+ path: pathKey,
+ summary: operation.summary || '',
+ tags: operation.tags || [],
+ };
+ // Extract compatibility version if present
+ if (operation['x-compatibility-version']) {
+ opMeta.compatVersion = operation['x-compatibility-version'];
+ }
+ // Extract externalDocs if present
+ if (operation.externalDocs) {
+ opMeta.externalDocs = {
+ description: operation.externalDocs.description || '',
+ url: operation.externalDocs.url,
+ };
+ }
+ operations.push(opMeta);
+ }
+ });
+ });
+ const article = createArticleDataForTag(tagOpenapi, operations, tagMeta);
+ article.fields.source = filePath;
+ article.fields.staticFilePath = filePath.replace(/^static\//, '/');
+ return article;
});
- const article = createArticleDataForTag(
- tagOpenapi,
- operations,
- tagMeta
- );
- article.fields.source = filePath;
- article.fields.staticFilePath = filePath.replace(/^static\//, '/');
- return article;
- });
- if (!fs.existsSync(targetPath)) {
- fs.mkdirSync(targetPath, { recursive: true });
+ if (!fs.existsSync(targetPath)) {
+ fs.mkdirSync(targetPath, { recursive: true });
+ }
+ const articleCollection = { articles };
+ // Write both YAML and JSON versions
+ const yamlPath = path.resolve(targetPath, 'articles.yml');
+ const jsonPath = path.resolve(targetPath, 'articles.json');
+ writeDataFile(articleCollection, yamlPath);
+ writeJsonFile(articleCollection, jsonPath);
+ console.log(`Generated ${articles.length} tag-based articles in ${targetPath}`);
+ }
+ catch (e) {
+ console.error('Error writing tag article data:', e);
}
- const articleCollection = { articles };
- // Write both YAML and JSON versions
- const yamlPath = path.resolve(targetPath, 'articles.yml');
- const jsonPath = path.resolve(targetPath, 'articles.json');
- writeDataFile(articleCollection, yamlPath);
- writeJsonFile(articleCollection, jsonPath);
- console.log(
- `Generated ${articles.length} tag-based articles in ${targetPath}`
- );
- } catch (e) {
- console.error('Error writing tag article data:', e);
- }
}
/**
* Generate Hugo data files from an OpenAPI specification grouped by tag
@@ -718,28 +674,24 @@ function writeOpenapiTagArticleData(sourcePath, targetPath, openapi, opts) {
* @param options - Generation options
*/
function generateHugoDataByTag(options) {
- const filenamePrefix = `${path.parse(options.specFile).name}-`;
- const sourceFile = readFile(options.specFile, 'utf8');
- // Optionally generate path-based files for backwards compatibility
- if (options.includePaths) {
- console.log(
- `\nGenerating OpenAPI path files in ${options.dataOutPath}....`
- );
- writePathOpenapis(sourceFile, filenamePrefix, options.dataOutPath);
- }
- // Generate tag-based files
- const tagOutPath = options.includePaths
- ? path.join(options.dataOutPath, 'tags')
- : options.dataOutPath;
- console.log(`\nGenerating OpenAPI tag files in ${tagOutPath}....`);
- writeTagOpenapis(sourceFile, filenamePrefix, tagOutPath);
- console.log(
- `\nGenerating OpenAPI tag article data in ${options.articleOutPath}...`
- );
- writeOpenapiTagArticleData(tagOutPath, options.articleOutPath, sourceFile, {
- filePattern: filenamePrefix,
- });
- console.log('\nTag-based generation complete!\n');
+ const filenamePrefix = `${path.parse(options.specFile).name}-`;
+ const sourceFile = readFile(options.specFile, 'utf8');
+ // Optionally generate path-based files for backwards compatibility
+ if (options.includePaths) {
+ console.log(`\nGenerating OpenAPI path files in ${options.dataOutPath}....`);
+ writePathOpenapis(sourceFile, filenamePrefix, options.dataOutPath);
+ }
+ // Generate tag-based files
+ const tagOutPath = options.includePaths
+ ? path.join(options.dataOutPath, 'tags')
+ : options.dataOutPath;
+ console.log(`\nGenerating OpenAPI tag files in ${tagOutPath}....`);
+ writeTagOpenapis(sourceFile, filenamePrefix, tagOutPath);
+ console.log(`\nGenerating OpenAPI tag article data in ${options.articleOutPath}...`);
+ writeOpenapiTagArticleData(tagOutPath, options.articleOutPath, sourceFile, {
+ filePattern: filenamePrefix,
+ });
+ console.log('\nTag-based generation complete!\n');
}
/**
* Generate Hugo data files from an OpenAPI specification
@@ -753,21 +705,19 @@ function generateHugoDataByTag(options) {
* @param options - Generation options
*/
function generateHugoData(options) {
- const filenamePrefix = `${path.parse(options.specFile).name}-`;
- const sourceFile = readFile(options.specFile, 'utf8');
- console.log(`\nGenerating OpenAPI path files in ${options.dataOutPath}....`);
- writePathOpenapis(sourceFile, filenamePrefix, options.dataOutPath);
- console.log(
- `\nGenerating OpenAPI article data in ${options.articleOutPath}...`
- );
- writeOpenapiArticleData(options.dataOutPath, options.articleOutPath, {
- filePattern: filenamePrefix,
- });
- console.log('\nGeneration complete!\n');
+ const filenamePrefix = `${path.parse(options.specFile).name}-`;
+ const sourceFile = readFile(options.specFile, 'utf8');
+ console.log(`\nGenerating OpenAPI path files in ${options.dataOutPath}....`);
+ writePathOpenapis(sourceFile, filenamePrefix, options.dataOutPath);
+ console.log(`\nGenerating OpenAPI article data in ${options.articleOutPath}...`);
+ writeOpenapiArticleData(options.dataOutPath, options.articleOutPath, {
+ filePattern: filenamePrefix,
+ });
+ console.log('\nGeneration complete!\n');
}
// CommonJS export for backward compatibility
module.exports = {
- generateHugoData,
- generateHugoDataByTag,
+ generateHugoData,
+ generateHugoDataByTag,
};
-//# sourceMappingURL=index.js.map
+//# sourceMappingURL=index.js.map
\ No newline at end of file
diff --git a/api-docs/scripts/generate-openapi-articles.ts b/api-docs/scripts/generate-openapi-articles.ts
index 1dca01e96..12db38fea 100644
--- a/api-docs/scripts/generate-openapi-articles.ts
+++ b/api-docs/scripts/generate-openapi-articles.ts
@@ -587,6 +587,7 @@ function generateOperationPages(options: GenerateOperationPagesOptions): void {
layout: 'operation',
// RapiDoc Mini configuration
specFile: tagSpecFile,
+ // RapiDoc match-paths format: "method /path" (e.g., "post /write")
matchPaths: `${method} ${op.path}`,
// Operation metadata
operationId: op.operationId,
diff --git a/api-docs/scripts/openapi-paths-to-hugo-data/index.ts b/api-docs/scripts/openapi-paths-to-hugo-data/index.ts
index 72bb051cc..8492230b6 100644
--- a/api-docs/scripts/openapi-paths-to-hugo-data/index.ts
+++ b/api-docs/scripts/openapi-paths-to-hugo-data/index.ts
@@ -498,7 +498,11 @@ function writeTagOpenapis(
HTTP_METHODS.forEach((method) => {
const operation = pathItem[method] as Operation | undefined;
if (operation?.tags?.includes(tagName)) {
- filteredPathItem[method] = operation;
+ // Clone the operation and restrict tags to only this tag
+ // This prevents RapiDoc from rendering the operation multiple times
+ // (once per tag) when an operation belongs to multiple tags
+ const filteredOperation = { ...operation, tags: [tagName] };
+ filteredPathItem[method] = filteredOperation;
hasOperations = true;
}
});
diff --git a/assets/js/components/rapidoc-mini.ts b/assets/js/components/rapidoc-mini.ts
index d4b4bc0f5..4a49eea9f 100644
--- a/assets/js/components/rapidoc-mini.ts
+++ b/assets/js/components/rapidoc-mini.ts
@@ -166,6 +166,7 @@ function createRapiDocElement(
// Core attributes
element.setAttribute('spec-url', specUrl);
+ // matchPaths format: "method /path" (e.g., "post /write")
if (matchPaths) {
element.setAttribute('match-paths', matchPaths);
}
diff --git a/data/article_data/influxdb/influxdb3_core/articles.json b/data/article_data/influxdb/influxdb3_core/articles.json
index 5d98cf414..ff6c0f9c0 100644
--- a/data/article_data/influxdb/influxdb3_core/articles.json
+++ b/data/article_data/influxdb/influxdb3_core/articles.json
@@ -1,5 +1,63 @@
{
"articles": [
+ {
+ "path": "api/auth-token",
+ "fields": {
+ "name": "Auth token",
+ "describes": [
+ "/api/v3/configure/token/admin",
+ "/api/v3/configure/token/admin/regenerate",
+ "/api/v3/configure/token",
+ "/api/v3/configure/token/named_admin"
+ ],
+ "title": "Auth token",
+ "description": "Manage tokens for authentication and authorization",
+ "tag": "Auth token",
+ "isConceptual": false,
+ "menuGroup": "Other",
+ "operations": [
+ {
+ "operationId": "PostCreateAdminToken",
+ "method": "POST",
+ "path": "/api/v3/configure/token/admin",
+ "summary": "Create admin token",
+ "tags": [
+ "Auth token"
+ ]
+ },
+ {
+ "operationId": "PostRegenerateAdminToken",
+ "method": "POST",
+ "path": "/api/v3/configure/token/admin/regenerate",
+ "summary": "Regenerate admin token",
+ "tags": [
+ "Auth token"
+ ]
+ },
+ {
+ "operationId": "DeleteToken",
+ "method": "DELETE",
+ "path": "/api/v3/configure/token",
+ "summary": "Delete token",
+ "tags": [
+ "Auth token"
+ ]
+ },
+ {
+ "operationId": "PostCreateNamedAdminToken",
+ "method": "POST",
+ "path": "/api/v3/configure/token/named_admin",
+ "summary": "Create named admin token",
+ "tags": [
+ "Auth token"
+ ]
+ }
+ ],
+ "tagDescription": "Manage tokens for authentication and authorization",
+ "source": "static/openapi/influxdb3-core/tags/tags/ref-auth-token.yaml",
+ "staticFilePath": "/openapi/influxdb3-core/tags/tags/ref-auth-token.yaml"
+ }
+ },
{
"path": "api/authentication",
"fields": {
@@ -22,8 +80,7 @@
"path": "/api/v3/configure/token/admin",
"summary": "Create admin token",
"tags": [
- "Authentication",
- "Token"
+ "Authentication"
]
},
{
@@ -32,8 +89,7 @@
"path": "/api/v3/configure/token/admin/regenerate",
"summary": "Regenerate admin token",
"tags": [
- "Authentication",
- "Token"
+ "Authentication"
]
},
{
@@ -42,8 +98,7 @@
"path": "/api/v3/configure/token",
"summary": "Delete token",
"tags": [
- "Authentication",
- "Token"
+ "Authentication"
]
},
{
@@ -52,8 +107,7 @@
"path": "/api/v3/configure/token/named_admin",
"summary": "Create named admin token",
"tags": [
- "Authentication",
- "Token"
+ "Authentication"
]
}
],
@@ -82,8 +136,7 @@
"path": "/api/v3/configure/distinct_cache",
"summary": "Create distinct cache",
"tags": [
- "Cache data",
- "Table"
+ "Cache data"
]
},
{
@@ -92,8 +145,7 @@
"path": "/api/v3/configure/distinct_cache",
"summary": "Delete distinct cache",
"tags": [
- "Cache data",
- "Table"
+ "Cache data"
]
},
{
@@ -102,8 +154,7 @@
"path": "/api/v3/configure/last_cache",
"summary": "Create last cache",
"tags": [
- "Cache data",
- "Table"
+ "Cache data"
]
},
{
@@ -112,8 +163,7 @@
"path": "/api/v3/configure/last_cache",
"summary": "Delete last cache",
"tags": [
- "Cache data",
- "Table"
+ "Cache data"
]
}
],
@@ -514,9 +564,7 @@
"fields": {
"name": "Table",
"describes": [
- "/api/v3/configure/table",
- "/api/v3/configure/distinct_cache",
- "/api/v3/configure/last_cache"
+ "/api/v3/configure/table"
],
"title": "Table",
"description": "Manage table schemas and data",
@@ -541,46 +589,6 @@
"tags": [
"Table"
]
- },
- {
- "operationId": "PostConfigureDistinctCache",
- "method": "POST",
- "path": "/api/v3/configure/distinct_cache",
- "summary": "Create distinct cache",
- "tags": [
- "Cache data",
- "Table"
- ]
- },
- {
- "operationId": "DeleteConfigureDistinctCache",
- "method": "DELETE",
- "path": "/api/v3/configure/distinct_cache",
- "summary": "Delete distinct cache",
- "tags": [
- "Cache data",
- "Table"
- ]
- },
- {
- "operationId": "PostConfigureLastCache",
- "method": "POST",
- "path": "/api/v3/configure/last_cache",
- "summary": "Create last cache",
- "tags": [
- "Cache data",
- "Table"
- ]
- },
- {
- "operationId": "DeleteConfigureLastCache",
- "method": "DELETE",
- "path": "/api/v3/configure/last_cache",
- "summary": "Delete last cache",
- "tags": [
- "Cache data",
- "Table"
- ]
}
],
"tagDescription": "Manage table schemas and data",
@@ -588,68 +596,6 @@
"staticFilePath": "/openapi/influxdb3-core/tags/tags/ref-table.yaml"
}
},
- {
- "path": "api/token",
- "fields": {
- "name": "Token",
- "describes": [
- "/api/v3/configure/token/admin",
- "/api/v3/configure/token/admin/regenerate",
- "/api/v3/configure/token",
- "/api/v3/configure/token/named_admin"
- ],
- "title": "Token",
- "description": "Manage tokens for authentication and authorization",
- "tag": "Token",
- "isConceptual": false,
- "menuGroup": "Administration",
- "operations": [
- {
- "operationId": "PostCreateAdminToken",
- "method": "POST",
- "path": "/api/v3/configure/token/admin",
- "summary": "Create admin token",
- "tags": [
- "Authentication",
- "Token"
- ]
- },
- {
- "operationId": "PostRegenerateAdminToken",
- "method": "POST",
- "path": "/api/v3/configure/token/admin/regenerate",
- "summary": "Regenerate admin token",
- "tags": [
- "Authentication",
- "Token"
- ]
- },
- {
- "operationId": "DeleteToken",
- "method": "DELETE",
- "path": "/api/v3/configure/token",
- "summary": "Delete token",
- "tags": [
- "Authentication",
- "Token"
- ]
- },
- {
- "operationId": "PostCreateNamedAdminToken",
- "method": "POST",
- "path": "/api/v3/configure/token/named_admin",
- "summary": "Create named admin token",
- "tags": [
- "Authentication",
- "Token"
- ]
- }
- ],
- "tagDescription": "Manage tokens for authentication and authorization",
- "source": "static/openapi/influxdb3-core/tags/tags/ref-token.yaml",
- "staticFilePath": "/openapi/influxdb3-core/tags/tags/ref-token.yaml"
- }
- },
{
"path": "api/write-data",
"fields": {
diff --git a/data/article_data/influxdb/influxdb3_core/articles.yml b/data/article_data/influxdb/influxdb3_core/articles.yml
index 3c7b297e1..c8f317c95 100644
--- a/data/article_data/influxdb/influxdb3_core/articles.yml
+++ b/data/article_data/influxdb/influxdb3_core/articles.yml
@@ -1,4 +1,45 @@
articles:
+ - path: api/auth-token
+ fields:
+ name: Auth token
+ describes:
+ - /api/v3/configure/token/admin
+ - /api/v3/configure/token/admin/regenerate
+ - /api/v3/configure/token
+ - /api/v3/configure/token/named_admin
+ title: Auth token
+ description: Manage tokens for authentication and authorization
+ tag: Auth token
+ isConceptual: false
+ menuGroup: Other
+ operations:
+ - operationId: PostCreateAdminToken
+ method: POST
+ path: /api/v3/configure/token/admin
+ summary: Create admin token
+ tags:
+ - Auth token
+ - operationId: PostRegenerateAdminToken
+ method: POST
+ path: /api/v3/configure/token/admin/regenerate
+ summary: Regenerate admin token
+ tags:
+ - Auth token
+ - operationId: DeleteToken
+ method: DELETE
+ path: /api/v3/configure/token
+ summary: Delete token
+ tags:
+ - Auth token
+ - operationId: PostCreateNamedAdminToken
+ method: POST
+ path: /api/v3/configure/token/named_admin
+ summary: Create named admin token
+ tags:
+ - Auth token
+ tagDescription: Manage tokens for authentication and authorization
+ source: static/openapi/influxdb3-core/tags/tags/ref-auth-token.yaml
+ staticFilePath: /openapi/influxdb3-core/tags/tags/ref-auth-token.yaml
- path: api/authentication
fields:
name: Authentication
@@ -38,28 +79,24 @@ articles:
summary: Create admin token
tags:
- Authentication
- - Token
- operationId: PostRegenerateAdminToken
method: POST
path: /api/v3/configure/token/admin/regenerate
summary: Regenerate admin token
tags:
- Authentication
- - Token
- operationId: DeleteToken
method: DELETE
path: /api/v3/configure/token
summary: Delete token
tags:
- Authentication
- - Token
- operationId: PostCreateNamedAdminToken
method: POST
path: /api/v3/configure/token/named_admin
summary: Create named admin token
tags:
- Authentication
- - Token
tagDescription: >
Depending on your workflow, use one of the following schemes to
authenticate to the InfluxDB 3 API:
@@ -162,28 +199,24 @@ articles:
summary: Create distinct cache
tags:
- Cache data
- - Table
- operationId: DeleteConfigureDistinctCache
method: DELETE
path: /api/v3/configure/distinct_cache
summary: Delete distinct cache
tags:
- Cache data
- - Table
- operationId: PostConfigureLastCache
method: POST
path: /api/v3/configure/last_cache
summary: Create last cache
tags:
- Cache data
- - Table
- operationId: DeleteConfigureLastCache
method: DELETE
path: /api/v3/configure/last_cache
summary: Delete last cache
tags:
- Cache data
- - Table
tagDescription: >
Manage the in-memory cache.
@@ -817,8 +850,6 @@ articles:
name: Table
describes:
- /api/v3/configure/table
- - /api/v3/configure/distinct_cache
- - /api/v3/configure/last_cache
title: Table
description: Manage table schemas and data
tag: Table
@@ -837,82 +868,9 @@ articles:
summary: Delete a table
tags:
- Table
- - operationId: PostConfigureDistinctCache
- method: POST
- path: /api/v3/configure/distinct_cache
- summary: Create distinct cache
- tags:
- - Cache data
- - Table
- - operationId: DeleteConfigureDistinctCache
- method: DELETE
- path: /api/v3/configure/distinct_cache
- summary: Delete distinct cache
- tags:
- - Cache data
- - Table
- - operationId: PostConfigureLastCache
- method: POST
- path: /api/v3/configure/last_cache
- summary: Create last cache
- tags:
- - Cache data
- - Table
- - operationId: DeleteConfigureLastCache
- method: DELETE
- path: /api/v3/configure/last_cache
- summary: Delete last cache
- tags:
- - Cache data
- - Table
tagDescription: Manage table schemas and data
source: static/openapi/influxdb3-core/tags/tags/ref-table.yaml
staticFilePath: /openapi/influxdb3-core/tags/tags/ref-table.yaml
- - path: api/token
- fields:
- name: Token
- describes:
- - /api/v3/configure/token/admin
- - /api/v3/configure/token/admin/regenerate
- - /api/v3/configure/token
- - /api/v3/configure/token/named_admin
- title: Token
- description: Manage tokens for authentication and authorization
- tag: Token
- isConceptual: false
- menuGroup: Administration
- operations:
- - operationId: PostCreateAdminToken
- method: POST
- path: /api/v3/configure/token/admin
- summary: Create admin token
- tags:
- - Authentication
- - Token
- - operationId: PostRegenerateAdminToken
- method: POST
- path: /api/v3/configure/token/admin/regenerate
- summary: Regenerate admin token
- tags:
- - Authentication
- - Token
- - operationId: DeleteToken
- method: DELETE
- path: /api/v3/configure/token
- summary: Delete token
- tags:
- - Authentication
- - Token
- - operationId: PostCreateNamedAdminToken
- method: POST
- path: /api/v3/configure/token/named_admin
- summary: Create named admin token
- tags:
- - Authentication
- - Token
- tagDescription: Manage tokens for authentication and authorization
- source: static/openapi/influxdb3-core/tags/tags/ref-token.yaml
- staticFilePath: /openapi/influxdb3-core/tags/tags/ref-token.yaml
- path: api/write-data
fields:
name: Write data
diff --git a/data/article_data/influxdb/influxdb3_enterprise/articles.json b/data/article_data/influxdb/influxdb3_enterprise/articles.json
index 834e5452a..7d9b658d4 100644
--- a/data/article_data/influxdb/influxdb3_enterprise/articles.json
+++ b/data/article_data/influxdb/influxdb3_enterprise/articles.json
@@ -1,5 +1,73 @@
{
"articles": [
+ {
+ "path": "api/auth-token",
+ "fields": {
+ "name": "Auth token",
+ "describes": [
+ "/api/v3/configure/enterprise/token",
+ "/api/v3/configure/token/admin",
+ "/api/v3/configure/token/admin/regenerate",
+ "/api/v3/configure/token",
+ "/api/v3/configure/token/named_admin"
+ ],
+ "title": "Auth token",
+ "description": "Manage tokens for authentication and authorization",
+ "tag": "Auth token",
+ "isConceptual": false,
+ "menuGroup": "Other",
+ "operations": [
+ {
+ "operationId": "PostCreateResourceToken",
+ "method": "POST",
+ "path": "/api/v3/configure/enterprise/token",
+ "summary": "Create a resource token",
+ "tags": [
+ "Auth token"
+ ]
+ },
+ {
+ "operationId": "PostCreateAdminToken",
+ "method": "POST",
+ "path": "/api/v3/configure/token/admin",
+ "summary": "Create admin token",
+ "tags": [
+ "Auth token"
+ ]
+ },
+ {
+ "operationId": "PostRegenerateAdminToken",
+ "method": "POST",
+ "path": "/api/v3/configure/token/admin/regenerate",
+ "summary": "Regenerate admin token",
+ "tags": [
+ "Auth token"
+ ]
+ },
+ {
+ "operationId": "DeleteToken",
+ "method": "DELETE",
+ "path": "/api/v3/configure/token",
+ "summary": "Delete token",
+ "tags": [
+ "Auth token"
+ ]
+ },
+ {
+ "operationId": "PostCreateNamedAdminToken",
+ "method": "POST",
+ "path": "/api/v3/configure/token/named_admin",
+ "summary": "Create named admin token",
+ "tags": [
+ "Auth token"
+ ]
+ }
+ ],
+ "tagDescription": "Manage tokens for authentication and authorization",
+ "source": "static/openapi/influxdb3-enterprise/tags/tags/ref-auth-token.yaml",
+ "staticFilePath": "/openapi/influxdb3-enterprise/tags/tags/ref-auth-token.yaml"
+ }
+ },
{
"path": "api/authentication",
"fields": {
@@ -23,8 +91,7 @@
"path": "/api/v3/configure/enterprise/token",
"summary": "Create a resource token",
"tags": [
- "Authentication",
- "Token"
+ "Authentication"
]
},
{
@@ -33,8 +100,7 @@
"path": "/api/v3/configure/token/admin",
"summary": "Create admin token",
"tags": [
- "Authentication",
- "Token"
+ "Authentication"
]
},
{
@@ -43,8 +109,7 @@
"path": "/api/v3/configure/token/admin/regenerate",
"summary": "Regenerate admin token",
"tags": [
- "Authentication",
- "Token"
+ "Authentication"
]
},
{
@@ -53,8 +118,7 @@
"path": "/api/v3/configure/token",
"summary": "Delete token",
"tags": [
- "Authentication",
- "Token"
+ "Authentication"
]
},
{
@@ -63,8 +127,7 @@
"path": "/api/v3/configure/token/named_admin",
"summary": "Create named admin token",
"tags": [
- "Authentication",
- "Token"
+ "Authentication"
]
}
],
@@ -93,8 +156,7 @@
"path": "/api/v3/configure/distinct_cache",
"summary": "Create distinct cache",
"tags": [
- "Cache data",
- "Table"
+ "Cache data"
]
},
{
@@ -103,8 +165,7 @@
"path": "/api/v3/configure/distinct_cache",
"summary": "Delete distinct cache",
"tags": [
- "Cache data",
- "Table"
+ "Cache data"
]
},
{
@@ -113,8 +174,7 @@
"path": "/api/v3/configure/last_cache",
"summary": "Create last cache",
"tags": [
- "Cache data",
- "Table"
+ "Cache data"
]
},
{
@@ -123,8 +183,7 @@
"path": "/api/v3/configure/last_cache",
"summary": "Delete last cache",
"tags": [
- "Cache data",
- "Table"
+ "Cache data"
]
}
],
@@ -545,9 +604,7 @@
"fields": {
"name": "Table",
"describes": [
- "/api/v3/configure/table",
- "/api/v3/configure/distinct_cache",
- "/api/v3/configure/last_cache"
+ "/api/v3/configure/table"
],
"title": "Table",
"description": "Manage table schemas and data",
@@ -581,46 +638,6 @@
"tags": [
"Table"
]
- },
- {
- "operationId": "PostConfigureDistinctCache",
- "method": "POST",
- "path": "/api/v3/configure/distinct_cache",
- "summary": "Create distinct cache",
- "tags": [
- "Cache data",
- "Table"
- ]
- },
- {
- "operationId": "DeleteConfigureDistinctCache",
- "method": "DELETE",
- "path": "/api/v3/configure/distinct_cache",
- "summary": "Delete distinct cache",
- "tags": [
- "Cache data",
- "Table"
- ]
- },
- {
- "operationId": "PostConfigureLastCache",
- "method": "POST",
- "path": "/api/v3/configure/last_cache",
- "summary": "Create last cache",
- "tags": [
- "Cache data",
- "Table"
- ]
- },
- {
- "operationId": "DeleteConfigureLastCache",
- "method": "DELETE",
- "path": "/api/v3/configure/last_cache",
- "summary": "Delete last cache",
- "tags": [
- "Cache data",
- "Table"
- ]
}
],
"tagDescription": "Manage table schemas and data",
@@ -628,79 +645,6 @@
"staticFilePath": "/openapi/influxdb3-enterprise/tags/tags/ref-table.yaml"
}
},
- {
- "path": "api/token",
- "fields": {
- "name": "Token",
- "describes": [
- "/api/v3/configure/enterprise/token",
- "/api/v3/configure/token/admin",
- "/api/v3/configure/token/admin/regenerate",
- "/api/v3/configure/token",
- "/api/v3/configure/token/named_admin"
- ],
- "title": "Token",
- "description": "Manage tokens for authentication and authorization",
- "tag": "Token",
- "isConceptual": false,
- "menuGroup": "Administration",
- "operations": [
- {
- "operationId": "PostCreateResourceToken",
- "method": "POST",
- "path": "/api/v3/configure/enterprise/token",
- "summary": "Create a resource token",
- "tags": [
- "Authentication",
- "Token"
- ]
- },
- {
- "operationId": "PostCreateAdminToken",
- "method": "POST",
- "path": "/api/v3/configure/token/admin",
- "summary": "Create admin token",
- "tags": [
- "Authentication",
- "Token"
- ]
- },
- {
- "operationId": "PostRegenerateAdminToken",
- "method": "POST",
- "path": "/api/v3/configure/token/admin/regenerate",
- "summary": "Regenerate admin token",
- "tags": [
- "Authentication",
- "Token"
- ]
- },
- {
- "operationId": "DeleteToken",
- "method": "DELETE",
- "path": "/api/v3/configure/token",
- "summary": "Delete token",
- "tags": [
- "Authentication",
- "Token"
- ]
- },
- {
- "operationId": "PostCreateNamedAdminToken",
- "method": "POST",
- "path": "/api/v3/configure/token/named_admin",
- "summary": "Create named admin token",
- "tags": [
- "Authentication",
- "Token"
- ]
- }
- ],
- "tagDescription": "Manage tokens for authentication and authorization",
- "source": "static/openapi/influxdb3-enterprise/tags/tags/ref-token.yaml",
- "staticFilePath": "/openapi/influxdb3-enterprise/tags/tags/ref-token.yaml"
- }
- },
{
"path": "api/write-data",
"fields": {
diff --git a/data/article_data/influxdb/influxdb3_enterprise/articles.yml b/data/article_data/influxdb/influxdb3_enterprise/articles.yml
index 2d502b7e9..acc7d4262 100644
--- a/data/article_data/influxdb/influxdb3_enterprise/articles.yml
+++ b/data/article_data/influxdb/influxdb3_enterprise/articles.yml
@@ -1,4 +1,52 @@
articles:
+ - path: api/auth-token
+ fields:
+ name: Auth token
+ describes:
+ - /api/v3/configure/enterprise/token
+ - /api/v3/configure/token/admin
+ - /api/v3/configure/token/admin/regenerate
+ - /api/v3/configure/token
+ - /api/v3/configure/token/named_admin
+ title: Auth token
+ description: Manage tokens for authentication and authorization
+ tag: Auth token
+ isConceptual: false
+ menuGroup: Other
+ operations:
+ - operationId: PostCreateResourceToken
+ method: POST
+ path: /api/v3/configure/enterprise/token
+ summary: Create a resource token
+ tags:
+ - Auth token
+ - operationId: PostCreateAdminToken
+ method: POST
+ path: /api/v3/configure/token/admin
+ summary: Create admin token
+ tags:
+ - Auth token
+ - operationId: PostRegenerateAdminToken
+ method: POST
+ path: /api/v3/configure/token/admin/regenerate
+ summary: Regenerate admin token
+ tags:
+ - Auth token
+ - operationId: DeleteToken
+ method: DELETE
+ path: /api/v3/configure/token
+ summary: Delete token
+ tags:
+ - Auth token
+ - operationId: PostCreateNamedAdminToken
+ method: POST
+ path: /api/v3/configure/token/named_admin
+ summary: Create named admin token
+ tags:
+ - Auth token
+ tagDescription: Manage tokens for authentication and authorization
+ source: static/openapi/influxdb3-enterprise/tags/tags/ref-auth-token.yaml
+ staticFilePath: /openapi/influxdb3-enterprise/tags/tags/ref-auth-token.yaml
- path: api/authentication
fields:
name: Authentication
@@ -37,35 +85,30 @@ articles:
summary: Create a resource token
tags:
- Authentication
- - Token
- operationId: PostCreateAdminToken
method: POST
path: /api/v3/configure/token/admin
summary: Create admin token
tags:
- Authentication
- - Token
- operationId: PostRegenerateAdminToken
method: POST
path: /api/v3/configure/token/admin/regenerate
summary: Regenerate admin token
tags:
- Authentication
- - Token
- operationId: DeleteToken
method: DELETE
path: /api/v3/configure/token
summary: Delete token
tags:
- Authentication
- - Token
- operationId: PostCreateNamedAdminToken
method: POST
path: /api/v3/configure/token/named_admin
summary: Create named admin token
tags:
- Authentication
- - Token
tagDescription: >
Depending on your workflow, use one of the following schemes to
authenticate to the InfluxDB 3 API:
@@ -159,28 +202,24 @@ articles:
summary: Create distinct cache
tags:
- Cache data
- - Table
- operationId: DeleteConfigureDistinctCache
method: DELETE
path: /api/v3/configure/distinct_cache
summary: Delete distinct cache
tags:
- Cache data
- - Table
- operationId: PostConfigureLastCache
method: POST
path: /api/v3/configure/last_cache
summary: Create last cache
tags:
- Cache data
- - Table
- operationId: DeleteConfigureLastCache
method: DELETE
path: /api/v3/configure/last_cache
summary: Delete last cache
tags:
- Cache data
- - Table
tagDescription: >
Manage the in-memory cache.
@@ -787,8 +826,6 @@ articles:
name: Table
describes:
- /api/v3/configure/table
- - /api/v3/configure/distinct_cache
- - /api/v3/configure/last_cache
title: Table
description: Manage table schemas and data
tag: Table
@@ -813,90 +850,9 @@ articles:
summary: Delete a table
tags:
- Table
- - operationId: PostConfigureDistinctCache
- method: POST
- path: /api/v3/configure/distinct_cache
- summary: Create distinct cache
- tags:
- - Cache data
- - Table
- - operationId: DeleteConfigureDistinctCache
- method: DELETE
- path: /api/v3/configure/distinct_cache
- summary: Delete distinct cache
- tags:
- - Cache data
- - Table
- - operationId: PostConfigureLastCache
- method: POST
- path: /api/v3/configure/last_cache
- summary: Create last cache
- tags:
- - Cache data
- - Table
- - operationId: DeleteConfigureLastCache
- method: DELETE
- path: /api/v3/configure/last_cache
- summary: Delete last cache
- tags:
- - Cache data
- - Table
tagDescription: Manage table schemas and data
source: static/openapi/influxdb3-enterprise/tags/tags/ref-table.yaml
staticFilePath: /openapi/influxdb3-enterprise/tags/tags/ref-table.yaml
- - path: api/token
- fields:
- name: Token
- describes:
- - /api/v3/configure/enterprise/token
- - /api/v3/configure/token/admin
- - /api/v3/configure/token/admin/regenerate
- - /api/v3/configure/token
- - /api/v3/configure/token/named_admin
- title: Token
- description: Manage tokens for authentication and authorization
- tag: Token
- isConceptual: false
- menuGroup: Administration
- operations:
- - operationId: PostCreateResourceToken
- method: POST
- path: /api/v3/configure/enterprise/token
- summary: Create a resource token
- tags:
- - Authentication
- - Token
- - operationId: PostCreateAdminToken
- method: POST
- path: /api/v3/configure/token/admin
- summary: Create admin token
- tags:
- - Authentication
- - Token
- - operationId: PostRegenerateAdminToken
- method: POST
- path: /api/v3/configure/token/admin/regenerate
- summary: Regenerate admin token
- tags:
- - Authentication
- - Token
- - operationId: DeleteToken
- method: DELETE
- path: /api/v3/configure/token
- summary: Delete token
- tags:
- - Authentication
- - Token
- - operationId: PostCreateNamedAdminToken
- method: POST
- path: /api/v3/configure/token/named_admin
- summary: Create named admin token
- tags:
- - Authentication
- - Token
- tagDescription: Manage tokens for authentication and authorization
- source: static/openapi/influxdb3-enterprise/tags/tags/ref-token.yaml
- staticFilePath: /openapi/influxdb3-enterprise/tags/tags/ref-token.yaml
- path: api/write-data
fields:
name: Write data
diff --git a/docs/plans/2024-12-10-standalone-operation-pages-design.md b/docs/plans/2024-12-10-standalone-operation-pages-design.md
new file mode 100644
index 000000000..70e619af2
--- /dev/null
+++ b/docs/plans/2024-12-10-standalone-operation-pages-design.md
@@ -0,0 +1,171 @@
+# Standalone API Operation Pages Design
+
+## Overview
+
+Create individual pages for each API operation with path-based URLs, rendered using RapiDoc Mini with existing tag-level OpenAPI specs.
+
+## Goals
+
+- **SEO/discoverability**: Each operation indexable with its own URL and metadata
+- **Deep linking**: Reliable bookmarkable/shareable URLs for specific operations
+- **Navigation UX**: Sidebar links navigate to actual pages (not hash fragments)
+- **Content customization**: Foundation for adding operation-specific guides and examples
+
+## URL Structure
+
+Path-based URLs with HTTP method as the final segment:
+
+| Operation | API Path | Page URL |
+| ------------------ | ----------------------------------- | ------------------------------------ |
+| PostV1Write | `POST /write` | `/api/write/post/` |
+| PostV2Write | `POST /api/v2/write` | `/api/v2/write/post/` |
+| PostWriteLP | `POST /api/v3/write_lp` | `/api/v3/write_lp/post/` |
+| GetV1ExecuteQuery | `GET /query` | `/api/query/get/` |
+| PostExecuteV1Query | `POST /query` | `/api/query/post/` |
+| GetExecuteQuerySQL | `GET /api/v3/query_sql` | `/api/v3/query_sql/get/` |
+| GetDatabases | `GET /api/v3/configure/database` | `/api/v3/configure/database/get/` |
+| DeleteDatabase | `DELETE /api/v3/configure/database` | `/api/v3/configure/database/delete/` |
+
+## Architecture
+
+### Existing Components (unchanged)
+
+- **Tag pages**: `/api/write-data/`, `/api/query-data/` etc. remain as landing pages
+- **Tag-level specs**: `static/openapi/influxdb-{product}/tags/tags/ref-{tag}.yaml` (\~30-50KB each)
+- **Sidebar structure**: Tag-based groups with operation summaries as link text
+
+### New Components
+
+1. **Operation page content files**: Generated at path-based locations
+2. **Operation page template**: Hugo layout using RapiDoc Mini
+3. **Updated sidebar links**: Point to path-based URLs instead of hash fragments
+
+## Content File Structure
+
+Generated operation pages at `content/influxdb3/{product}/api/{path}/{method}/_index.md`:
+
+```yaml
+---
+title: Write line protocol (v1-compatible)
+description: Write data using InfluxDB v1-compatible line protocol endpoint
+type: api-operation
+layout: operation
+# RapiDoc Mini configuration
+specFile: /openapi/influxdb-influxdb3_core/tags/tags/ref-write-data.yaml
+matchPaths: post /write
+# Operation metadata
+operationId: PostV1Write
+method: POST
+apiPath: /write
+tag: Write data
+compatVersion: v1
+# Links
+related:
+ - /influxdb3/core/write-data/http-api/compatibility-apis/
+---
+```
+
+## Hugo Template
+
+New layout `layouts/api-operation/operation.html`:
+
+```html
+{{ define "main" }}
+{{ .Title }}
+
+
{{ . | markdownify }}
- {{ else }} - {{ with .Description }} + {{/* Summary/Description - skip for conceptual pages (shown in content section) */}} + {{ if not (.Params.isConceptual | default false) }} + {{ with .Params.summary }}{{ . | markdownify }}
+ {{ else }} + {{ with .Description }} +{{ . | markdownify }}
+ {{ end }} {{ end }} {{ end }} diff --git a/layouts/partials/api/rapidoc-mini.html b/layouts/partials/api/rapidoc-mini.html index c29b82351..2d7251077 100644 --- a/layouts/partials/api/rapidoc-mini.html +++ b/layouts/partials/api/rapidoc-mini.html @@ -95,8 +95,9 @@ rapi-doc::part(section-tag) { } /* Fix parameter table layout - reduce indentation from empty td cells */ +/* Match site's body font size (16px) for consistency */ rapi-doc { - --font-size-regular: 14px; + --font-size-regular: 16px; } /* Fix auth schemes at narrow widths - ensure content is scrollable */ diff --git a/layouts/partials/sidebar/api-menu-items.html b/layouts/partials/sidebar/api-menu-items.html index 3bde1d9b2..204f5b924 100644 --- a/layouts/partials/sidebar/api-menu-items.html +++ b/layouts/partials/sidebar/api-menu-items.html @@ -133,7 +133,12 @@ {{ if and (reflect.IsMap $fields) (isset $fields "operations") }} {{ $operations = index $fields "operations" }} {{ end }} - {{ $hasOperations := gt (len $operations) 0 }} + {{/* Don't show operations for conceptual/traitTag pages (e.g., Authentication) */}} + {{ $isConceptual := false }} + {{ if and (reflect.IsMap $fields) (isset $fields "isConceptual") }} + {{ $isConceptual = index $fields "isConceptual" }} + {{ end }} + {{ $hasOperations := and (gt (len $operations) 0) (not $isConceptual) }}