feat(api): add inline curl examples and Ask AI links to API operations
Generate curl examples at Hugo template time from OpenAPI specs, with server URL variable resolution, $ref handling, and JSON body generation from schema properties. Add "Ask AI about this example" links using existing Kapa integration. Add client library related link to all InfluxDB 3 API tag pages. Improve visual separation between operations with distinct badge colors and response body indentation.docs-v2-pr6622
parent
895d56058d
commit
df9c9977cf
|
|
@ -29,7 +29,7 @@ info:
|
|||
name: InfluxData
|
||||
url: https://www.influxdata.com
|
||||
email: support@influxdata.com
|
||||
x-influxdata-related:
|
||||
x-related:
|
||||
- title: Migrate from InfluxDB v1 or v2
|
||||
href: /influxdb3/core/get-started/migrate-from-influxdb-v1-v2/
|
||||
servers:
|
||||
|
|
@ -68,7 +68,7 @@ tags:
|
|||
values to cache, the maximum number of distinct value combinations to cache, and
|
||||
the maximum age of cached values. A DVC is associated with a table, which can
|
||||
have multiple DVCs.
|
||||
x-influxdata-related:
|
||||
x-related:
|
||||
- title: Manage the Distinct Value Cache
|
||||
href: /influxdb3/core/admin/distinct-value-cache/
|
||||
- name: Cache last value
|
||||
|
|
@ -83,7 +83,7 @@ tags:
|
|||
what fields to cache, what tags to use to identify each series, and the
|
||||
number of values to cache for each unique series.
|
||||
An LVC is associated with a table, which can have multiple LVCs.
|
||||
x-influxdata-related:
|
||||
x-related:
|
||||
- title: Manage the Last Value Cache
|
||||
href: /influxdb3/core/admin/last-value-cache/
|
||||
- name: Migrate from InfluxDB v1 or v2
|
||||
|
|
@ -190,6 +190,11 @@ tags:
|
|||
- name: Auth token
|
||||
description: Manage tokens for authentication and authorization
|
||||
- name: Write data
|
||||
x-related:
|
||||
- title: Write data using HTTP APIs
|
||||
href: /influxdb3/core/write-data/http-api/
|
||||
- title: Line protocol reference
|
||||
href: /influxdb3/core/reference/syntax/line-protocol/
|
||||
description: |
|
||||
Write data to InfluxDB 3 Core using line protocol format.
|
||||
|
||||
|
|
@ -331,7 +336,7 @@ paths:
|
|||
- QuerystringAuthentication: []
|
||||
tags:
|
||||
- Write data
|
||||
x-influxdata-related:
|
||||
x-related:
|
||||
- title: Use compatibility APIs to write data
|
||||
href: /influxdb3/core/write-data/http-api/compatibility-apis/
|
||||
/api/v2/write:
|
||||
|
|
@ -422,7 +427,7 @@ paths:
|
|||
- TokenAuthentication: []
|
||||
tags:
|
||||
- Write data
|
||||
x-influxdata-related:
|
||||
x-related:
|
||||
- title: Use compatibility APIs to write data
|
||||
href: /influxdb3/core/write-data/http-api/compatibility-apis/
|
||||
/api/v3/write_lp:
|
||||
|
|
@ -816,7 +821,7 @@ paths:
|
|||
- QuerystringAuthentication: []
|
||||
tags:
|
||||
- Query data
|
||||
x-influxdata-related:
|
||||
x-related:
|
||||
- title: Use the InfluxDB v1 HTTP query API and InfluxQL to query data
|
||||
href: /influxdb3/core/query-data/execute-queries/influxdb-v1-api/
|
||||
post:
|
||||
|
|
@ -938,7 +943,7 @@ paths:
|
|||
- QuerystringAuthentication: []
|
||||
tags:
|
||||
- Query data
|
||||
x-influxdata-related:
|
||||
x-related:
|
||||
- title: Use the InfluxDB v1 HTTP query API and InfluxQL to query data
|
||||
href: /influxdb3/core/query-data/execute-queries/influxdb-v1-api/
|
||||
/health:
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ info:
|
|||
name: InfluxData
|
||||
url: https://www.influxdata.com
|
||||
email: support@influxdata.com
|
||||
x-influxdata-related:
|
||||
x-related:
|
||||
- title: Migrate from InfluxDB v1 or v2
|
||||
href: /influxdb3/enterprise/get-started/migrate-from-influxdb-v1-v2/
|
||||
servers:
|
||||
|
|
@ -321,7 +321,7 @@ paths:
|
|||
tags:
|
||||
|
||||
- Write data
|
||||
x-influxdata-related:
|
||||
x-related:
|
||||
- title: Use compatibility APIs to write data
|
||||
href: /influxdb3/enterprise/write-data/http-api/compatibility-apis/
|
||||
/api/v2/write:
|
||||
|
|
@ -414,7 +414,7 @@ paths:
|
|||
tags:
|
||||
|
||||
- Write data
|
||||
x-influxdata-related:
|
||||
x-related:
|
||||
- title: Use compatibility APIs to write data
|
||||
href: /influxdb3/enterprise/write-data/http-api/compatibility-apis/
|
||||
/api/v3/write_lp:
|
||||
|
|
@ -838,7 +838,7 @@ paths:
|
|||
tags:
|
||||
- Query data
|
||||
|
||||
x-influxdata-related:
|
||||
x-related:
|
||||
- title: Use the InfluxDB v1 HTTP query API and InfluxQL to query data
|
||||
href: /influxdb3/enterprise/query-data/execute-queries/influxdb-v1-api/
|
||||
post:
|
||||
|
|
@ -957,7 +957,7 @@ paths:
|
|||
tags:
|
||||
- Query data
|
||||
|
||||
x-influxdata-related:
|
||||
x-related:
|
||||
- title: Use the InfluxDB v1 HTTP query API and InfluxQL to query data
|
||||
href: /influxdb3/enterprise/query-data/execute-queries/influxdb-v1-api/
|
||||
/health:
|
||||
|
|
|
|||
|
|
@ -7,16 +7,20 @@
|
|||
* for all InfluxDB products.
|
||||
*
|
||||
* This script:
|
||||
* 1. Runs getswagger.sh to fetch/bundle OpenAPI specs
|
||||
* 2. Copies specs to static directory for download
|
||||
* 3. Generates path group fragments (YAML and JSON)
|
||||
* 4. Creates article metadata (YAML and JSON)
|
||||
* 5. Generates Hugo content pages from article data
|
||||
* 1. Cleans output directories (unless --no-clean)
|
||||
* 2. Runs getswagger.sh to fetch/bundle OpenAPI specs
|
||||
* 3. Copies specs to static directory for download
|
||||
* 4. Generates path group fragments (YAML and JSON)
|
||||
* 5. Creates article metadata (YAML and JSON)
|
||||
* 6. Generates Hugo content pages from article data
|
||||
*
|
||||
* Usage:
|
||||
* node generate-openapi-articles.js # Generate all products
|
||||
* node generate-openapi-articles.js cloud-v2 # Generate single product
|
||||
* node generate-openapi-articles.js cloud-v2 oss-v2 # Generate multiple products
|
||||
* node generate-openapi-articles.js # Clean and generate all products
|
||||
* node generate-openapi-articles.js cloud-v2 # Clean and generate single product
|
||||
* node generate-openapi-articles.js --no-clean # Generate without cleaning
|
||||
* node generate-openapi-articles.js --dry-run # Preview what would be cleaned
|
||||
* node generate-openapi-articles.js --skip-fetch # Skip getswagger.sh fetch step
|
||||
* node generate-openapi-articles.js --validate-links # Validate documentation links
|
||||
*
|
||||
* @module generate-openapi-articles
|
||||
*/
|
||||
|
|
@ -586,6 +590,25 @@ All {{% product-name %}} API endpoints, sorted by path.
|
|||
) {
|
||||
frontmatter.related = article.fields.related;
|
||||
}
|
||||
// Add client library related link for InfluxDB 3 products
|
||||
if (contentPath.includes('influxdb3/') && !isConceptual) {
|
||||
// Extract product segment from contentPath (e.g., "core" from ".../influxdb3/core")
|
||||
const influxdb3Match = contentPath.match(/influxdb3\/([^/]+)/);
|
||||
if (influxdb3Match) {
|
||||
const productSegment = influxdb3Match[1];
|
||||
const clientLibLink = {
|
||||
title: 'InfluxDB 3 API client libraries',
|
||||
href: `/influxdb3/${productSegment}/reference/client-libraries/v3/`,
|
||||
};
|
||||
const existing = frontmatter.related || [];
|
||||
const alreadyHas = existing.some(
|
||||
(r) => typeof r === 'object' && r.href === clientLibLink.href
|
||||
);
|
||||
if (!alreadyHas) {
|
||||
frontmatter.related = [...existing, clientLibLink];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Add alt_links for cross-product API navigation
|
||||
if (apiProductsMap.size > 0) {
|
||||
const altLinks = {};
|
||||
|
|
@ -641,12 +664,16 @@ function mergeArticleData(articlesFiles, outputPath) {
|
|||
...article.fields.operations,
|
||||
];
|
||||
}
|
||||
// Merge related links
|
||||
// Merge related links (dedup by href for both strings and objects)
|
||||
if (article.fields.related && article.fields.related.length > 0) {
|
||||
const existingRelated = existing.fields.related || [];
|
||||
const newRelated = article.fields.related.filter(
|
||||
(r) => !existingRelated.includes(r)
|
||||
const existingHrefs = new Set(
|
||||
existingRelated.map((r) => (typeof r === 'string' ? r : r.href))
|
||||
);
|
||||
const newRelated = article.fields.related.filter((r) => {
|
||||
const href = typeof r === 'string' ? r : r.href;
|
||||
return !existingHrefs.has(href);
|
||||
});
|
||||
existing.fields.related = [...existingRelated, ...newRelated];
|
||||
}
|
||||
// Keep the longest/most detailed description
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -557,7 +557,7 @@ function generateTagPagesFromArticleData(
|
|||
menuGroup?: string;
|
||||
staticFilePath?: string;
|
||||
operations?: OperationMeta[];
|
||||
related?: string[];
|
||||
related?: (string | { title: string; href: string })[];
|
||||
weight?: number;
|
||||
};
|
||||
}>;
|
||||
|
|
@ -738,6 +738,27 @@ All {{% product-name %}} API endpoints, sorted by path.
|
|||
frontmatter.related = article.fields.related;
|
||||
}
|
||||
|
||||
// Add client library related link for InfluxDB 3 products
|
||||
if (contentPath.includes('influxdb3/') && !isConceptual) {
|
||||
// Extract product segment from contentPath (e.g., "core" from ".../influxdb3/core")
|
||||
const influxdb3Match = contentPath.match(/influxdb3\/([^/]+)/);
|
||||
if (influxdb3Match) {
|
||||
const productSegment = influxdb3Match[1];
|
||||
const clientLibLink = {
|
||||
title: 'InfluxDB 3 API client libraries',
|
||||
href: `/influxdb3/${productSegment}/reference/client-libraries/v3/`,
|
||||
};
|
||||
const existing =
|
||||
(frontmatter.related as Array<{ title: string; href: string }>) || [];
|
||||
const alreadyHas = existing.some(
|
||||
(r) => typeof r === 'object' && r.href === clientLibLink.href
|
||||
);
|
||||
if (!alreadyHas) {
|
||||
frontmatter.related = [...existing, clientLibLink];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add alt_links for cross-product API navigation
|
||||
if (apiProductsMap.size > 0) {
|
||||
const altLinks: Record<string, string> = {};
|
||||
|
|
@ -781,7 +802,7 @@ interface ArticleData {
|
|||
menuGroup?: string;
|
||||
staticFilePath?: string;
|
||||
operations?: OperationMeta[];
|
||||
related?: string[];
|
||||
related?: (string | { title: string; href: string })[];
|
||||
source?: string;
|
||||
};
|
||||
}>;
|
||||
|
|
@ -827,12 +848,16 @@ function mergeArticleData(articlesFiles: string[], outputPath: string): void {
|
|||
];
|
||||
}
|
||||
|
||||
// Merge related links
|
||||
// Merge related links (dedup by href for both strings and objects)
|
||||
if (article.fields.related && article.fields.related.length > 0) {
|
||||
const existingRelated = existing.fields.related || [];
|
||||
const newRelated = article.fields.related.filter(
|
||||
(r) => !existingRelated.includes(r)
|
||||
const existingHrefs = new Set(
|
||||
existingRelated.map((r) => (typeof r === 'string' ? r : r.href))
|
||||
);
|
||||
const newRelated = article.fields.related.filter((r) => {
|
||||
const href = typeof r === 'string' ? r : r.href;
|
||||
return !existingHrefs.has(href);
|
||||
});
|
||||
existing.fields.related = [...existingRelated, ...newRelated];
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,6 +11,14 @@ import * as yaml from 'js-yaml';
|
|||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
/**
|
||||
* Related link with title and href (from x-influxdata-related)
|
||||
*/
|
||||
interface RelatedLink {
|
||||
title: string;
|
||||
href: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* OpenAPI path item object
|
||||
*/
|
||||
|
|
@ -41,8 +49,10 @@ interface Operation {
|
|||
externalDocs?: ExternalDocs;
|
||||
/** Compatibility version for migration context (v1 or v2) */
|
||||
'x-compatibility-version'?: string;
|
||||
/** Related documentation links (replaces inline "Related guides" sections) */
|
||||
/** Related documentation links as plain URLs */
|
||||
'x-influxdatadocs-related'?: string[];
|
||||
/** Related documentation links with title and href */
|
||||
'x-related'?: RelatedLink[];
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
|
|
@ -218,8 +228,10 @@ interface Tag {
|
|||
externalDocs?: ExternalDocs;
|
||||
/** Indicates this is a conceptual/supplementary tag (no operations) */
|
||||
'x-traitTag'?: boolean;
|
||||
/** Related documentation links (replaces inline "Related guides" sections) */
|
||||
/** Related documentation links as plain URLs */
|
||||
'x-influxdatadocs-related'?: string[];
|
||||
/** Related documentation links with title and href */
|
||||
'x-related'?: RelatedLink[];
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
|
|
@ -239,8 +251,10 @@ interface OperationMeta {
|
|||
description: string;
|
||||
url: string;
|
||||
};
|
||||
/** Related documentation links */
|
||||
/** Related documentation links (plain URLs) */
|
||||
related?: string[];
|
||||
/** Related documentation links with title and href */
|
||||
relatedLinks?: RelatedLink[];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -265,8 +279,8 @@ interface Article {
|
|||
tags?: string[];
|
||||
source?: string;
|
||||
staticFilePath?: string;
|
||||
/** Related documentation links extracted from x-relatedLinks */
|
||||
related?: string[];
|
||||
/** Related documentation links (plain URLs or {title, href} objects) */
|
||||
related?: (string | RelatedLink)[];
|
||||
/** OpenAPI tags from operations (for Hugo frontmatter) */
|
||||
apiTags?: string[];
|
||||
/** Menu display name (actual endpoint path, different from Hugo path) */
|
||||
|
|
@ -473,6 +487,11 @@ function extractOperationsByTag(
|
|||
opMeta.related = operation['x-influxdatadocs-related'];
|
||||
}
|
||||
|
||||
// Extract x-related (title/href objects) if present
|
||||
if (operation['x-related'] && Array.isArray(operation['x-related'])) {
|
||||
opMeta.relatedLinks = operation['x-related'] as RelatedLink[];
|
||||
}
|
||||
|
||||
// Add operation to each of its tags
|
||||
(operation.tags || []).forEach((tag) => {
|
||||
if (!tagOperations.has(tag)) {
|
||||
|
|
@ -1115,36 +1134,51 @@ function createArticleDataForTag(
|
|||
article.fields.weight = 100;
|
||||
}
|
||||
|
||||
// Aggregate unique related URLs from multiple sources into article-level related
|
||||
// Aggregate related links from multiple sources into article-level related
|
||||
// This populates Hugo frontmatter `related` field for "Related content" links
|
||||
const relatedUrls = new Set<string>();
|
||||
// Supports both plain URL strings and {title, href} objects
|
||||
const relatedItems: (string | RelatedLink)[] = [];
|
||||
const seenHrefs = new Set<string>();
|
||||
|
||||
// First check tag-level x-influxdatadocs-related
|
||||
if (tagMeta?.['x-influxdatadocs-related']) {
|
||||
(tagMeta['x-influxdatadocs-related'] as string[]).forEach((url) =>
|
||||
relatedUrls.add(url)
|
||||
);
|
||||
}
|
||||
|
||||
// Then check tag-level externalDocs (legacy single link)
|
||||
if (tagMeta?.externalDocs?.url) {
|
||||
relatedUrls.add(tagMeta.externalDocs.url);
|
||||
}
|
||||
|
||||
// Then aggregate from operations
|
||||
operations.forEach((op) => {
|
||||
// Check operation-level x-influxdatadocs-related (via opMeta.related)
|
||||
if (op.related) {
|
||||
op.related.forEach((url) => relatedUrls.add(url));
|
||||
// Helper to add a link, deduplicating by href
|
||||
const addRelated = (item: string | RelatedLink): void => {
|
||||
const href = typeof item === 'string' ? item : item.href;
|
||||
if (!seenHrefs.has(href)) {
|
||||
seenHrefs.add(href);
|
||||
relatedItems.push(item);
|
||||
}
|
||||
};
|
||||
|
||||
// Tag-level x-related ({title, href} objects)
|
||||
if (tagMeta?.['x-related']) {
|
||||
(tagMeta['x-related'] as RelatedLink[]).forEach(addRelated);
|
||||
}
|
||||
|
||||
// Tag-level x-influxdatadocs-related (plain URLs)
|
||||
if (tagMeta?.['x-influxdatadocs-related']) {
|
||||
(tagMeta['x-influxdatadocs-related'] as string[]).forEach(addRelated);
|
||||
}
|
||||
|
||||
// Tag-level externalDocs (legacy single link)
|
||||
if (tagMeta?.externalDocs?.url) {
|
||||
addRelated(tagMeta.externalDocs.url);
|
||||
}
|
||||
|
||||
// Operation-level related links
|
||||
operations.forEach((op) => {
|
||||
if (op.relatedLinks) {
|
||||
op.relatedLinks.forEach(addRelated);
|
||||
}
|
||||
if (op.related) {
|
||||
op.related.forEach(addRelated);
|
||||
}
|
||||
// Check operation-level externalDocs (legacy single link)
|
||||
if (op.externalDocs?.url) {
|
||||
relatedUrls.add(op.externalDocs.url);
|
||||
addRelated(op.externalDocs.url);
|
||||
}
|
||||
});
|
||||
|
||||
if (relatedUrls.size > 0) {
|
||||
article.fields.related = Array.from(relatedUrls);
|
||||
if (relatedItems.length > 0) {
|
||||
article.fields.related = relatedItems;
|
||||
}
|
||||
|
||||
return article;
|
||||
|
|
@ -1231,6 +1265,14 @@ function writeOpenapiTagArticleData(
|
|||
opMeta.related = operation['x-influxdatadocs-related'];
|
||||
}
|
||||
|
||||
// Extract x-related (title/href objects)
|
||||
if (
|
||||
operation['x-related'] &&
|
||||
Array.isArray(operation['x-related'])
|
||||
) {
|
||||
opMeta.relatedLinks = operation['x-related'] as RelatedLink[];
|
||||
}
|
||||
|
||||
operations.push(opMeta);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,67 @@
|
|||
// API Code Samples
|
||||
// Styles for inline curl examples and Ask AI links within API operations
|
||||
|
||||
.api-code-sample {
|
||||
margin: $api-spacing-lg 0;
|
||||
border: 1px solid rgba(0, 0, 0, 0.1);
|
||||
border-radius: $api-border-radius;
|
||||
overflow: hidden;
|
||||
|
||||
.dark-theme & {
|
||||
border-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.api-code-sample-header {
|
||||
margin: 0;
|
||||
padding: $api-spacing-sm $api-spacing-md;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
background: rgba(0, 0, 0, 0.03);
|
||||
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
|
||||
|
||||
.dark-theme & {
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
border-bottom-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
.api-code-sample-body {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
pre.api-code-block {
|
||||
margin: 0;
|
||||
padding: $api-spacing-md;
|
||||
padding-bottom: $api-spacing-lg + 0.5rem;
|
||||
overflow-x: auto;
|
||||
background: #1e1e2e;
|
||||
color: #cdd6f4;
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.5;
|
||||
border-radius: 0;
|
||||
|
||||
code {
|
||||
background: none;
|
||||
padding: 0;
|
||||
color: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
|
||||
.api-code-ask-ai {
|
||||
position: absolute;
|
||||
right: $api-spacing-md;
|
||||
bottom: $api-spacing-sm;
|
||||
font-size: 0.8rem;
|
||||
color: #89b4fa;
|
||||
text-decoration: none;
|
||||
opacity: 0.7;
|
||||
transition: opacity 0.2s;
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -8,17 +8,17 @@ $api-spacing-md: 1rem;
|
|||
$api-spacing-lg: 1.5rem;
|
||||
$api-spacing-xl: 2rem;
|
||||
|
||||
// Method colors (matching existing theme)
|
||||
// Method colors
|
||||
$method-get: #00A3FF;
|
||||
$method-post: #34BB55;
|
||||
$method-put: #FFB94A;
|
||||
$method-delete: #F95F53;
|
||||
$method-delete: #D63031;
|
||||
$method-patch: #9b2aff;
|
||||
|
||||
// Status code colors
|
||||
// Status code colors — intentionally distinct from method colors
|
||||
$status-success: #34BB55;
|
||||
$status-redirect: #FFB94A;
|
||||
$status-client-error: #F95F53;
|
||||
$status-client-error: #E17055;
|
||||
$status-server-error: #9b2aff;
|
||||
|
||||
// ============================================
|
||||
|
|
@ -31,8 +31,8 @@ $status-server-error: #9b2aff;
|
|||
|
||||
.api-operation {
|
||||
margin-bottom: $api-spacing-xl;
|
||||
padding-top: $api-spacing-lg;
|
||||
border-top: 1px solid $nav-border;
|
||||
padding-top: $api-spacing-xl;
|
||||
border-top: 2px solid $nav-border;
|
||||
|
||||
&:first-child {
|
||||
border-top: none;
|
||||
|
|
@ -430,7 +430,10 @@ $status-server-error: #9b2aff;
|
|||
}
|
||||
|
||||
.api-response-body {
|
||||
padding-top: $api-spacing-sm;
|
||||
margin-top: $api-spacing-sm;
|
||||
margin-left: $api-spacing-lg;
|
||||
padding: $api-spacing-sm 0 $api-spacing-sm $api-spacing-md;
|
||||
border-left: 2px solid rgba($article-text, 0.1);
|
||||
}
|
||||
|
||||
// ============================================
|
||||
|
|
|
|||
|
|
@ -35,7 +35,8 @@
|
|||
"layouts/v3-wayfinding",
|
||||
"layouts/api-layout",
|
||||
"layouts/api-security-schemes",
|
||||
"layouts/api-operations";
|
||||
"layouts/api-operations",
|
||||
"layouts/api-code-samples";
|
||||
|
||||
// Import Components
|
||||
@import "components/influxdb-version-detector",
|
||||
|
|
|
|||
|
|
@ -93,6 +93,9 @@ operations:
|
|||
summary: Delete an authorization
|
||||
tags:
|
||||
- Authorizations (API tokens)
|
||||
related:
|
||||
- title: InfluxDB 3 API client libraries
|
||||
href: /influxdb3/cloud-serverless/reference/client-libraries/v3/
|
||||
alt_links:
|
||||
core: /influxdb3/core/api/
|
||||
enterprise: /influxdb3/enterprise/api/
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ operations:
|
|||
summary: Update a measurement schema
|
||||
tags:
|
||||
- Bucket Schemas
|
||||
related:
|
||||
- title: InfluxDB 3 API client libraries
|
||||
href: /influxdb3/cloud-serverless/reference/client-libraries/v3/
|
||||
alt_links:
|
||||
core: /influxdb3/core/api/
|
||||
enterprise: /influxdb3/enterprise/api/
|
||||
|
|
|
|||
|
|
@ -112,6 +112,9 @@ operations:
|
|||
summary: Remove an owner from a bucket
|
||||
tags:
|
||||
- Buckets
|
||||
related:
|
||||
- title: InfluxDB 3 API client libraries
|
||||
href: /influxdb3/cloud-serverless/reference/client-libraries/v3/
|
||||
alt_links:
|
||||
core: /influxdb3/core/api/
|
||||
enterprise: /influxdb3/enterprise/api/
|
||||
|
|
|
|||
|
|
@ -63,6 +63,9 @@ operations:
|
|||
summary: Delete a database retention policy
|
||||
tags:
|
||||
- DBRPs
|
||||
related:
|
||||
- title: InfluxDB 3 API client libraries
|
||||
href: /influxdb3/cloud-serverless/reference/client-libraries/v3/
|
||||
alt_links:
|
||||
core: /influxdb3/core/api/
|
||||
enterprise: /influxdb3/enterprise/api/
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ operations:
|
|||
summary: Delete data
|
||||
tags:
|
||||
- Delete
|
||||
related:
|
||||
- title: InfluxDB 3 API client libraries
|
||||
href: /influxdb3/cloud-serverless/reference/client-libraries/v3/
|
||||
alt_links:
|
||||
core: /influxdb3/core/api/
|
||||
enterprise: /influxdb3/enterprise/api/
|
||||
|
|
|
|||
|
|
@ -69,6 +69,9 @@ operations:
|
|||
summary: Find script parameters.
|
||||
tags:
|
||||
- Invokable Scripts
|
||||
related:
|
||||
- title: InfluxDB 3 API client libraries
|
||||
href: /influxdb3/cloud-serverless/reference/client-libraries/v3/
|
||||
alt_links:
|
||||
core: /influxdb3/core/api/
|
||||
enterprise: /influxdb3/enterprise/api/
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ operations:
|
|||
summary: Retrieve limits for an organization
|
||||
tags:
|
||||
- Limits
|
||||
related:
|
||||
- title: InfluxDB 3 API client libraries
|
||||
href: /influxdb3/cloud-serverless/reference/client-libraries/v3/
|
||||
alt_links:
|
||||
core: /influxdb3/core/api/
|
||||
enterprise: /influxdb3/enterprise/api/
|
||||
|
|
|
|||
|
|
@ -82,6 +82,9 @@ operations:
|
|||
summary: Remove an owner from an organization
|
||||
tags:
|
||||
- Organizations
|
||||
related:
|
||||
- title: InfluxDB 3 API client libraries
|
||||
href: /influxdb3/cloud-serverless/reference/client-libraries/v3/
|
||||
alt_links:
|
||||
core: /influxdb3/core/api/
|
||||
enterprise: /influxdb3/enterprise/api/
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ operations:
|
|||
summary: List all known resources
|
||||
tags:
|
||||
- Resources
|
||||
related:
|
||||
- title: InfluxDB 3 API client libraries
|
||||
href: /influxdb3/cloud-serverless/reference/client-libraries/v3/
|
||||
alt_links:
|
||||
core: /influxdb3/core/api/
|
||||
enterprise: /influxdb3/enterprise/api/
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ operations:
|
|||
summary: List all top level routes
|
||||
tags:
|
||||
- Routes
|
||||
related:
|
||||
- title: InfluxDB 3 API client libraries
|
||||
href: /influxdb3/cloud-serverless/reference/client-libraries/v3/
|
||||
alt_links:
|
||||
core: /influxdb3/core/api/
|
||||
enterprise: /influxdb3/enterprise/api/
|
||||
|
|
|
|||
|
|
@ -34,6 +34,9 @@ operations:
|
|||
summary: Delete secrets from an organization
|
||||
tags:
|
||||
- Secrets
|
||||
related:
|
||||
- title: InfluxDB 3 API client libraries
|
||||
href: /influxdb3/cloud-serverless/reference/client-libraries/v3/
|
||||
alt_links:
|
||||
core: /influxdb3/core/api/
|
||||
enterprise: /influxdb3/enterprise/api/
|
||||
|
|
|
|||
|
|
@ -40,6 +40,9 @@ operations:
|
|||
summary: Delete an authorization
|
||||
tags:
|
||||
- Security and access endpoints
|
||||
related:
|
||||
- title: InfluxDB 3 API client libraries
|
||||
href: /influxdb3/cloud-serverless/reference/client-libraries/v3/
|
||||
alt_links:
|
||||
core: /influxdb3/core/api/
|
||||
enterprise: /influxdb3/enterprise/api/
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ operations:
|
|||
summary: List all known resources
|
||||
tags:
|
||||
- System information endpoints
|
||||
related:
|
||||
- title: InfluxDB 3 API client libraries
|
||||
href: /influxdb3/cloud-serverless/reference/client-libraries/v3/
|
||||
alt_links:
|
||||
core: /influxdb3/core/api/
|
||||
enterprise: /influxdb3/enterprise/api/
|
||||
|
|
|
|||
|
|
@ -165,6 +165,9 @@ operations:
|
|||
summary: Retry a task run
|
||||
tags:
|
||||
- Tasks
|
||||
related:
|
||||
- title: InfluxDB 3 API client libraries
|
||||
href: /influxdb3/cloud-serverless/reference/client-libraries/v3/
|
||||
alt_links:
|
||||
core: /influxdb3/core/api/
|
||||
enterprise: /influxdb3/enterprise/api/
|
||||
|
|
|
|||
|
|
@ -94,6 +94,9 @@ operations:
|
|||
summary: Remove an owner from a Telegraf config
|
||||
tags:
|
||||
- Telegrafs
|
||||
related:
|
||||
- title: InfluxDB 3 API client libraries
|
||||
href: /influxdb3/cloud-serverless/reference/client-libraries/v3/
|
||||
alt_links:
|
||||
core: /influxdb3/core/api/
|
||||
enterprise: /influxdb3/enterprise/api/
|
||||
|
|
|
|||
|
|
@ -87,6 +87,9 @@ operations:
|
|||
summary: Export a new template
|
||||
tags:
|
||||
- Templates
|
||||
related:
|
||||
- title: InfluxDB 3 API client libraries
|
||||
href: /influxdb3/cloud-serverless/reference/client-libraries/v3/
|
||||
alt_links:
|
||||
core: /influxdb3/core/api/
|
||||
enterprise: /influxdb3/enterprise/api/
|
||||
|
|
|
|||
|
|
@ -16,6 +16,9 @@ operations:
|
|||
summary: Retrieve usage for an organization
|
||||
tags:
|
||||
- Usage
|
||||
related:
|
||||
- title: InfluxDB 3 API client libraries
|
||||
href: /influxdb3/cloud-serverless/reference/client-libraries/v3/
|
||||
alt_links:
|
||||
core: /influxdb3/core/api/
|
||||
enterprise: /influxdb3/enterprise/api/
|
||||
|
|
|
|||
|
|
@ -64,6 +64,9 @@ operations:
|
|||
summary: Delete a label from a variable
|
||||
tags:
|
||||
- Variables
|
||||
related:
|
||||
- title: InfluxDB 3 API client libraries
|
||||
href: /influxdb3/cloud-serverless/reference/client-libraries/v3/
|
||||
alt_links:
|
||||
core: /influxdb3/core/api/
|
||||
enterprise: /influxdb3/enterprise/api/
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
* 1. API reference pages (link validation, content structure)
|
||||
* 2. 3-column layout with TOC (for InfluxDB 3 Core/Enterprise)
|
||||
* 3. Hugo-native tag page rendering
|
||||
* 4. Related links from OpenAPI x-related → frontmatter → rendered HTML
|
||||
*
|
||||
* Run with:
|
||||
* node cypress/support/run-e2e-specs.js --spec "cypress/e2e/content/api-reference.cy.js" content/influxdb3/core/reference/api/_index.md
|
||||
|
|
@ -346,11 +347,11 @@ describe('All endpoints page', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('operation cards link to tag pages with method anchors', () => {
|
||||
it('operation cards link to tag pages with operation anchors', () => {
|
||||
cy.get('.api-operation-card')
|
||||
.first()
|
||||
.should('have.attr', 'href')
|
||||
.and('match', /\/api\/.*\/#(get|post|put|patch|delete)-/i);
|
||||
.and('match', /\/api\/.*\/#operation\//);
|
||||
});
|
||||
|
||||
it('is accessible from navigation', () => {
|
||||
|
|
@ -363,3 +364,167 @@ describe('All endpoints page', () => {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* API Code Sample Tests
|
||||
* Tests that inline curl examples render correctly on tag pages
|
||||
*/
|
||||
describe('API code samples', () => {
|
||||
const tagPages = [
|
||||
'/influxdb3/core/api/write-data/',
|
||||
'/influxdb3/enterprise/api/write-data/',
|
||||
];
|
||||
|
||||
tagPages.forEach((page) => {
|
||||
describe(`Code samples on ${page}`, () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('GET', '**', (req) => {
|
||||
req.continue((res) => {
|
||||
if (res.headers['content-type']?.includes('text/html')) {
|
||||
res.body = res.body.replace(
|
||||
/data-user-analytics-fingerprint-enabled="true"/,
|
||||
'data-user-analytics-fingerprint-enabled="false"'
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
cy.visit(page);
|
||||
});
|
||||
|
||||
it('each operation has a code sample', () => {
|
||||
cy.get('.api-operation').each(($op) => {
|
||||
cy.wrap($op).find('.api-code-sample').should('have.length', 1);
|
||||
});
|
||||
});
|
||||
|
||||
it('code samples have header and code block', () => {
|
||||
cy.get('.api-code-sample')
|
||||
.first()
|
||||
.within(() => {
|
||||
cy.get('.api-code-sample-header').should(
|
||||
'contain',
|
||||
'Example request'
|
||||
);
|
||||
cy.get('.api-code-block code').should('exist');
|
||||
});
|
||||
});
|
||||
|
||||
it('code block contains a curl command', () => {
|
||||
cy.get('.api-code-block code')
|
||||
.first()
|
||||
.invoke('text')
|
||||
.should('match', /curl --request (GET|POST|PUT|PATCH|DELETE)/);
|
||||
});
|
||||
|
||||
it('curl command includes Authorization header', () => {
|
||||
cy.get('.api-code-block code')
|
||||
.first()
|
||||
.invoke('text')
|
||||
.should('include', 'Authorization: Bearer INFLUX_TOKEN');
|
||||
});
|
||||
|
||||
it('POST operations include request body in curl', () => {
|
||||
cy.get('.api-operation[data-method="post"]')
|
||||
.first()
|
||||
.find('.api-code-block code')
|
||||
.invoke('text')
|
||||
.should('include', '--data-raw');
|
||||
});
|
||||
|
||||
it('code samples have Ask AI links', () => {
|
||||
cy.get('.api-code-sample .api-code-ask-ai')
|
||||
.first()
|
||||
.should('have.attr', 'data-query')
|
||||
.and('not.be.empty');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* API Client Library Related Link Tests
|
||||
* Tests that InfluxDB 3 tag pages include client library related links
|
||||
*/
|
||||
describe('API client library related links', () => {
|
||||
const influxdb3Pages = [
|
||||
'/influxdb3/core/api/write-data/',
|
||||
'/influxdb3/enterprise/api/write-data/',
|
||||
];
|
||||
|
||||
influxdb3Pages.forEach((page) => {
|
||||
describe(`Client library link on ${page}`, () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('GET', '**', (req) => {
|
||||
req.continue((res) => {
|
||||
if (res.headers['content-type']?.includes('text/html')) {
|
||||
res.body = res.body.replace(
|
||||
/data-user-analytics-fingerprint-enabled="true"/,
|
||||
'data-user-analytics-fingerprint-enabled="false"'
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
cy.visit(page);
|
||||
});
|
||||
|
||||
it('includes InfluxDB 3 API client libraries in related links', () => {
|
||||
cy.get('.related ul li a')
|
||||
.filter(':contains("InfluxDB 3 API client libraries")')
|
||||
.should('have.length', 1)
|
||||
.and('have.attr', 'href')
|
||||
.and('match', /\/influxdb3\/\w+\/reference\/client-libraries\/v3\//);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* API Related Links Tests
|
||||
* Tests that x-related from OpenAPI specs renders as related links on tag pages
|
||||
*/
|
||||
describe('API related links', () => {
|
||||
const pagesWithRelated = ['/influxdb3/core/api/write-data/'];
|
||||
|
||||
pagesWithRelated.forEach((page) => {
|
||||
describe(`Related links on ${page}`, () => {
|
||||
beforeEach(() => {
|
||||
cy.intercept('GET', '**', (req) => {
|
||||
req.continue((res) => {
|
||||
if (res.headers['content-type']?.includes('text/html')) {
|
||||
res.body = res.body.replace(
|
||||
/data-user-analytics-fingerprint-enabled="true"/,
|
||||
'data-user-analytics-fingerprint-enabled="false"'
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
cy.visit(page);
|
||||
});
|
||||
|
||||
it('displays a related section', () => {
|
||||
cy.get('.related').should('exist');
|
||||
cy.get('.related h4#related').should('contain', 'Related');
|
||||
});
|
||||
|
||||
it('renders related links from x-related as anchor elements', () => {
|
||||
cy.get('.related ul li a').should('have.length.at.least', 2);
|
||||
});
|
||||
|
||||
it('related links have title text and valid href', () => {
|
||||
cy.get('.related ul li a').each(($a) => {
|
||||
// Each link has non-empty text
|
||||
cy.wrap($a).invoke('text').should('not.be.empty');
|
||||
// Each link has an href starting with /
|
||||
cy.wrap($a).should('have.attr', 'href').and('match', /^\//);
|
||||
});
|
||||
});
|
||||
|
||||
it('related links resolve to valid pages', () => {
|
||||
cy.get('.related ul li a').each(($a) => {
|
||||
const href = $a.attr('href');
|
||||
cy.request(href).its('status').should('eq', 200);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,455 +1,125 @@
|
|||
# API Code Samples, Ask AI, & Client Library Integration Plan
|
||||
# API Code Samples & Ask AI Integration Plan
|
||||
|
||||
## Context
|
||||
## Scope
|
||||
|
||||
The docs-v2 site recently migrated from RapiDoc to Hugo-native API templates. The current API pages render operation details (method, path, parameters, request body, response schemas) but have **no code samples**. This plan adds:
|
||||
This plan covers:
|
||||
|
||||
1. **curl request examples** generated at build time, displayed **inline within each operation** in the article flow
|
||||
2. **Response body schema** for the successful response, shown inline below the request example
|
||||
3. **"Ask AI about this example"** button on code blocks (API code samples first, then all code blocks site-wide)
|
||||
4. **Client library integration** (phased — links first, code samples later)
|
||||
1. **Inline curl examples** for each API operation, generated at Hugo template time from the OpenAPI spec
|
||||
2. **"Ask AI about this example"** link on each curl example, using the existing Kapa integration
|
||||
3. **Client library related link** on all InfluxDB 3 API tag pages
|
||||
|
||||
## Design Decision: Inline Code Samples (Not Sidebar)
|
||||
**Out of scope** (separate plans):
|
||||
|
||||
Code samples render **within the article flow** inside each operation block, not crammed into the right sidebar. The TOC stays at its current 200px width, unchanged.
|
||||
- Site-wide Ask AI on all code blocks (render-codeblock hook)
|
||||
- Client library tabbed code samples with language tabs
|
||||
- Duplicate response schema rendering (already shown in Responses section)
|
||||
|
||||
Each operation block gets a code sample section at the bottom:
|
||||
***
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────────────────┐
|
||||
│ POST /api/v2/write │
|
||||
│ ───────────────── │
|
||||
│ Summary: Write data │
|
||||
│ Description: ... │
|
||||
│ │
|
||||
│ Parameters │
|
||||
│ ┌────────┬──────┬────────────────────────┐ │
|
||||
│ │ Name │ Type │ Description │ │
|
||||
│ └────────┴──────┴────────────────────────┘ │
|
||||
│ │
|
||||
│ Request Body │
|
||||
│ ┌────────────────────────────────────────┐ │
|
||||
│ │ schema details... │ │
|
||||
│ └────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ Responses │
|
||||
│ ┌────────────────────────────────────────┐ │
|
||||
│ │ 204 Success │ │
|
||||
│ │ 400 Bad Request │ │
|
||||
│ └────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─ Example ──────────────────────────────┐ │
|
||||
│ │ ┌──────────────────────────────────┐ │ │
|
||||
│ │ │ curl --request POST \ │ │ │
|
||||
│ │ │ "http://localhost:8181/..." \ │ │ │
|
||||
│ │ │ --header "Authorization: ..." \│ │ │
|
||||
│ │ │ --header "Content-Type: ..." \ │ │ │
|
||||
│ │ │ --data-raw "mem,host=a v=1.0" │ │ │
|
||||
│ │ └──────────────────────────────────┘ │ │
|
||||
│ │ [Ask AI about this example] │ │
|
||||
│ └────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─ Client Libraries ────────────────────┐ │
|
||||
│ │ [Python] [JavaScript] [Go] [Java] [C#]│ │
|
||||
│ │ ┌──────────────────────────────────┐ │ │
|
||||
│ │ │ from influxdb_client_3 import │ │ │
|
||||
│ │ │ InfluxDBClient3 │ │ │
|
||||
│ │ │ client = InfluxDBClient3(...) │ │ │
|
||||
│ │ │ client.write(record="...") │ │ │
|
||||
│ │ └──────────────────────────────────┘ │ │
|
||||
│ │ 📖 Full guide: Write data with │ │
|
||||
│ │ client libraries → │ │
|
||||
│ └────────────────────────────────────────┘ │
|
||||
│ │
|
||||
│ ┌─ Response ──────────────────────────────┐ │
|
||||
│ │ 200 OK │ │
|
||||
│ │ ┌──────────────────────────────────┐ │ │
|
||||
│ │ │ id string │ │ │
|
||||
│ │ │ name string │ │ │
|
||||
│ │ │ orgID string │ │ │
|
||||
│ │ │ ... │ │ │
|
||||
│ │ └──────────────────────────────────┘ │ │
|
||||
│ │ ──── or ──── │ │
|
||||
│ │ 204 No Content (empty body) │ │
|
||||
│ └────────────────────────────────────────┘ │
|
||||
└──────────────────────────────────────────────────┘
|
||||
```
|
||||
## Architecture
|
||||
|
||||
---
|
||||
**No build script changes for curl generation.** The curl example is constructed entirely in a Hugo partial (`api/code-sample.html`) using data already loaded by `tag-renderer.html` — the full parsed OpenAPI spec with server URLs, parameters, request body schemas, and examples.
|
||||
|
||||
## Phase 1: curl Examples in Article Flow (MVP)
|
||||
The existing `influxdb-url.js` automatically replaces the default placeholder host in `<pre>` elements with the user's custom URL. No new JavaScript is needed for URL personalization.
|
||||
|
||||
### 1.1 Generate curl examples in build script
|
||||
### Operation layout order (revised)
|
||||
|
||||
**Modify:** `api-docs/scripts/generate-openapi-articles.ts`
|
||||
1. Header (method + path + summary)
|
||||
2. Description
|
||||
3. Parameters
|
||||
4. Request Body
|
||||
5. **Example (curl + Ask AI)** — new
|
||||
6. Responses
|
||||
|
||||
For each operation, generate a `curlExample` string and store it in the operation metadata. The build script already processes every operation to extract `operationId`, `method`, `path`, `summary`, and `tags` for frontmatter.
|
||||
***
|
||||
|
||||
Add a function `generateCurlExample(operation, spec)` that:
|
||||
1. Gets server URL from `spec.servers[0].url` (fallback: `http://localhost:8181`)
|
||||
2. Builds the full URL with path parameter placeholders (e.g., `{bucketID}`)
|
||||
3. Adds required query parameters with placeholder values
|
||||
4. Adds `Authorization: Bearer $INFLUX_TOKEN` header
|
||||
5. Adds `Content-Type` header when request body exists
|
||||
6. Generates request body from:
|
||||
- `requestBody.content["application/json"].schema.example` (first choice)
|
||||
- `requestBody.content["application/json"].example` (second choice)
|
||||
- A minimal JSON object from `required` + `properties` with type-based defaults (fallback)
|
||||
- For `text/plain` content types (like line protocol), uses a sample line protocol string
|
||||
7. Stores the complete curl command as a multiline string
|
||||
## curl Example Generation
|
||||
|
||||
The operation frontmatter entry becomes:
|
||||
```yaml
|
||||
operations:
|
||||
- operationId: PostWrite
|
||||
method: POST
|
||||
path: /api/v2/write
|
||||
summary: Write data
|
||||
tags: [Write]
|
||||
curlExample: |
|
||||
curl --request POST \
|
||||
"http://localhost:8181/api/v2/write?bucket=DATABASE_NAME&precision=ns" \
|
||||
--header "Authorization: Bearer $INFLUX_TOKEN" \
|
||||
--header "Content-Type: text/plain; charset=utf-8" \
|
||||
--data-raw "mem,host=host1 used_percent=23.43234543 1556896326"
|
||||
```
|
||||
### Partial: `layouts/partials/api/code-sample.html`
|
||||
|
||||
### 1.2 Create curl display partial
|
||||
Receives the operation definition (`$opDef`), spec (`$spec`), and operation metadata from `operation.html`. Constructs a curl command:
|
||||
|
||||
**New file:** `layouts/partials/api/code-sample.html`
|
||||
1. **Server URL**: `spec.servers[0].url` — falls back to the product's `placeholder_host`. The existing `influxdb-url.js` replaces this in the DOM if the user has a custom URL.
|
||||
2. **Method**: Always explicit `--request METHOD`
|
||||
3. **Path**: Appended to server URL. `{param}` placeholders left as-is in the URL.
|
||||
4. **Query parameters**: Only required ones. Uses `example` value if available, otherwise an `UPPER_SNAKE_CASE` placeholder derived from the parameter name.
|
||||
5. **Headers**:
|
||||
- Always: `--header "Authorization: Bearer INFLUX_TOKEN"`
|
||||
- When request body exists: `--header "Content-Type: ..."` derived from the first key in `requestBody.content`
|
||||
6. **Request body**:
|
||||
- `application/json`: Uses `schema.example` if present. If no example, body is omitted entirely — no synthesized fake data.
|
||||
- `text/plain` (line protocol): Hardcoded sample: `--data-raw "measurement,tag=value field=1.0"`
|
||||
- No example and no special content type: body omitted, shows only URL + headers.
|
||||
|
||||
Renders the code sample block within the operation. Receives the operation dict (with `curlExample` field) and page context.
|
||||
### Ask AI link
|
||||
|
||||
Output:
|
||||
```html
|
||||
<div class="api-code-sample">
|
||||
<div class="api-code-sample-header">
|
||||
<h5>Example</h5>
|
||||
</div>
|
||||
<div class="api-code-sample-body">
|
||||
<pre class="api-code-block"><code class="language-sh">{{ .operation.curlExample }}</code></pre>
|
||||
<a href="#" class="ask-ai-open api-code-ask-ai"
|
||||
data-query="Explain this API request: POST /api/v2/write - Write data">
|
||||
Ask AI about this example
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
### 1.3 Integrate into operation.html
|
||||
|
||||
**Modify:** `layouts/partials/api/operation.html`
|
||||
|
||||
After the Responses section, add:
|
||||
```html
|
||||
{{ with .operation.curlExample }}
|
||||
{{ partial "api/code-sample.html" (dict "operation" $.operation "context" $.context) }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
### 1.4 Add styles
|
||||
|
||||
**New file:** `assets/styles/layouts/_api-code-samples.scss`
|
||||
|
||||
Key styles:
|
||||
- `.api-code-sample`: full-width block within the operation, subtle border/background
|
||||
- `.api-code-sample-header`: section header with "Example" title
|
||||
- `.api-code-block`: dark background code block (always dark, regardless of theme), horizontal scroll for overflow
|
||||
- `.api-code-ask-ai`: small link below code block, uses existing ask-ai-open styling conventions
|
||||
- Smooth transition/animation when expanding
|
||||
|
||||
**Modify:** `assets/styles/styles-default.scss` — add import for `_api-code-samples.scss`.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Successful Response Schema
|
||||
|
||||
### 2.1 Render the response body schema for the successful status code
|
||||
|
||||
Instead of generating response *examples*, show the **response body schema** for the successful response (typically `200` or `201`) directly below the curl request example.
|
||||
|
||||
This reuses the existing `responses.html` and `schema.html` partials, which already resolve `$ref` and render schema properties with types and descriptions. The difference is we show only the **successful response** in the code sample section (rather than all status codes, which are already shown in the Responses section above).
|
||||
|
||||
**Approach:**
|
||||
- No build script changes needed — the schema is already in the OpenAPI spec, parsed at Hugo build time
|
||||
- The `code-sample.html` partial reads the operation from the spec (already loaded by `tag-renderer.html`), finds the first 2xx response, and renders its body schema
|
||||
|
||||
**Modify:** `layouts/partials/api/code-sample.html`
|
||||
|
||||
Below the curl request, add:
|
||||
```html
|
||||
{{ $successResponse := false }}
|
||||
{{ range $code, $resp := $opDef.responses }}
|
||||
{{ if hasPrefix $code "2" }}
|
||||
{{ $successResponse = $resp }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ with $successResponse }}
|
||||
{{ $content := .content | default dict }}
|
||||
{{ $jsonContent := index $content "application/json" | default dict }}
|
||||
{{ with $jsonContent.schema }}
|
||||
<div class="api-code-response">
|
||||
<h5>Response Body</h5>
|
||||
{{ partial "api/schema.html" (dict "schema" . "spec" $spec "level" 0) }}
|
||||
</div>
|
||||
{{ else }}
|
||||
<div class="api-code-response api-code-response--empty">
|
||||
<h5>Response</h5>
|
||||
<p>No response body</p>
|
||||
</div>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
```
|
||||
|
||||
This means for a `204 No Content` response (like the write endpoint), it shows "No response body." For a `200 OK` with a JSON schema (like list endpoints), it renders the full schema properties table — the same compact rendering already used in the Responses section but focused on just the success case.
|
||||
|
||||
**Note:** This requires passing the full operation definition and spec to `code-sample.html`, not just the frontmatter operation metadata. The partial needs access to the parsed spec to resolve the response schema. This is already available within `tag-renderer.html` where `code-sample.html` is called.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: "Ask AI about this example" — All Code Blocks
|
||||
|
||||
### 3.1 API code samples (included in Phase 1)
|
||||
|
||||
Already covered — each code sample gets an "Ask AI about this example" link using the existing `ask-ai-open` class and `data-query` attribute, leveraging the Kapa.ai widget.
|
||||
|
||||
### 3.2 Hugo render hook for all code blocks
|
||||
|
||||
**New file:** `layouts/_default/_markup/render-codeblock.html`
|
||||
|
||||
A Hugo markdown render hook that wraps every fenced code block with an optional "Ask AI" link:
|
||||
Each code sample block includes an "Ask AI about this example" link using the existing `ask-ai-open` CSS class and `data-query` attribute. The existing `ask-ai-trigger.js` handles click events and opens the Kapa widget — no new JavaScript needed.
|
||||
|
||||
```html
|
||||
<div class="code-block-wrapper">
|
||||
<pre><code class="language-{{ .Type }}">{{ .Inner }}</code></pre>
|
||||
{{ if .Type }}
|
||||
<a href="#" class="ask-ai-open code-block-ask-ai"
|
||||
data-query="Explain this {{ .Type }} code example from the {{ $.Page.Title }} documentation: {{ .Inner | truncate 200 }}">
|
||||
Ask AI about this example
|
||||
</a>
|
||||
{{ end }}
|
||||
</div>
|
||||
<a href="#" class="ask-ai-open api-code-ask-ai"
|
||||
data-query="Explain this API request: POST /api/v2/write - Write data">
|
||||
Ask AI about this example
|
||||
</a>
|
||||
```
|
||||
|
||||
Considerations:
|
||||
- Only show on code blocks with a language identifier (skip plain text blocks)
|
||||
- Truncate the code in the query to avoid overly long prompts
|
||||
- Use page title for context
|
||||
- Add subtle styling (small text, appears on hover or below the code block)
|
||||
- Test with existing code block rendering (syntax highlighting, copy button, etc.)
|
||||
- Must not break existing `pytest-codeblocks` annotations or other code block features
|
||||
***
|
||||
|
||||
### 3.3 Add styles for code block Ask AI
|
||||
## Client Library Related Link
|
||||
|
||||
**Modify:** `assets/styles/layouts/_api-code-samples.scss` (or create a separate partial)
|
||||
The generation script adds a related link to `/influxdb3/{product}/reference/client-libraries/v3/` for all InfluxDB 3 product tag pages.
|
||||
|
||||
```scss
|
||||
.code-block-ask-ai {
|
||||
display: block;
|
||||
font-size: 0.75rem;
|
||||
color: $nav-item;
|
||||
text-decoration: none;
|
||||
padding: 0.25rem 0;
|
||||
opacity: 0.6;
|
||||
transition: opacity 0.2s;
|
||||
**InfluxDB 3 products** (identified by `pagesDir` containing `influxdb3/`):
|
||||
|
||||
&:hover {
|
||||
opacity: 1;
|
||||
color: $nav-item-hover;
|
||||
}
|
||||
}
|
||||
```
|
||||
- `influxdb3_core`
|
||||
- `influxdb3_enterprise`
|
||||
- `cloud-dedicated`
|
||||
- `cloud-serverless`
|
||||
- `clustered`
|
||||
|
||||
---
|
||||
**Excluded** (future plan with v2 client library links):
|
||||
|
||||
## Phase 4: Client Library Integration
|
||||
- `cloud-v2`, `oss-v2`, `oss-v1`, `enterprise-v1`
|
||||
|
||||
Multi-language code tabs are **only for client libraries** — not general-purpose language examples. The curl example (Phase 1) covers the raw HTTP request. The client library section helps users accomplish the same task using the InfluxDB 3 client libraries (Python, JavaScript, Go, Java, C#).
|
||||
The `{product}` segment is derived from the `pagesDir` (e.g., `content/influxdb3/core` yields `core`).
|
||||
|
||||
Users and agents can create their own language-specific boilerplate for direct HTTP calls, but the client libraries abstract away HTTP details and provide idiomatic interfaces — that's what we want to surface here.
|
||||
***
|
||||
|
||||
### 4.1 Client library links on relevant operations
|
||||
## File Changes
|
||||
|
||||
**New file:** `data/api-client-library-links.yml`
|
||||
### New files
|
||||
|
||||
A mapping from operation tags/paths to client library documentation:
|
||||
```yaml
|
||||
write:
|
||||
description: "Write data using InfluxDB 3 client libraries"
|
||||
operations:
|
||||
- PostWrite
|
||||
- PostWriteV1
|
||||
links:
|
||||
- name: Python
|
||||
url: /influxdb3/{version}/reference/client-libraries/v3/python/
|
||||
guide: /influxdb3/{version}/write-data/client-libraries/
|
||||
- name: JavaScript
|
||||
url: /influxdb3/{version}/reference/client-libraries/v3/javascript/
|
||||
- name: Go
|
||||
url: /influxdb3/{version}/reference/client-libraries/v3/go/
|
||||
- name: Java
|
||||
url: /influxdb3/{version}/reference/client-libraries/v3/java/
|
||||
- name: C#
|
||||
url: /influxdb3/{version}/reference/client-libraries/v3/csharp/
|
||||
query:
|
||||
operations:
|
||||
- PostQuery
|
||||
links:
|
||||
- name: Python
|
||||
url: /influxdb3/{version}/reference/client-libraries/v3/python/
|
||||
# ...
|
||||
```
|
||||
| File | Purpose |
|
||||
| ---------------------------------------------- | ---------------------------- |
|
||||
| `layouts/partials/api/code-sample.html` | curl example + Ask AI link |
|
||||
| `assets/styles/layouts/_api-code-samples.scss` | Styles for code sample block |
|
||||
|
||||
**Modify:** `layouts/partials/api/code-sample.html`
|
||||
### Modified files
|
||||
|
||||
Below the curl example, render a "Client Libraries" section when the operation matches. This section includes:
|
||||
1. Links to the relevant client library reference pages
|
||||
2. A link to the relevant guide (e.g., "Write data with client libraries")
|
||||
| File | Change |
|
||||
| ----------------------------------------------- | ------------------------------------------------------------------ |
|
||||
| `layouts/partials/api/operation.html` | Insert `code-sample.html` between request body and responses |
|
||||
| `assets/styles/styles-default.scss` | Import `_api-code-samples.scss` |
|
||||
| `api-docs/scripts/generate-openapi-articles.ts` | Add client library reference related link for InfluxDB 3 tag pages |
|
||||
|
||||
```html
|
||||
<div class="api-client-libraries">
|
||||
<h6>Client Libraries</h6>
|
||||
<ul>
|
||||
<li><a href="/influxdb3/core/reference/client-libraries/v3/python/">Python</a></li>
|
||||
<li><a href="...">JavaScript</a></li>
|
||||
...
|
||||
</ul>
|
||||
<p><a href="/influxdb3/core/write-data/client-libraries/">Full guide: Write data with client libraries</a></p>
|
||||
</div>
|
||||
```
|
||||
### Not modified
|
||||
|
||||
### 4.2 Client library code samples with language tabs
|
||||
| File | Reason |
|
||||
| ------------------------------------------------------ | ------------------------ |
|
||||
| `layouts/api/list.html` | No layout changes needed |
|
||||
| `assets/js/main.js` | No new JS components |
|
||||
| `assets/js/components/api-toc.ts` | TOC unchanged |
|
||||
| `assets/styles/layouts/_api-layout.scss` | Layout unchanged |
|
||||
| `api-docs/scripts/openapi-paths-to-hugo-data/index.ts` | No data model changes |
|
||||
|
||||
Add tabbed code samples showing how to accomplish the same operation using each InfluxDB 3 client library. These are **not** raw HTTP examples in different languages — they show the idiomatic client library usage.
|
||||
|
||||
**Scope:** Only operations that have corresponding client library methods:
|
||||
- **Write** operations: `PostWrite`, `PostWriteV1` — client library `write()` / `writeRecord()` methods
|
||||
- **Query** operations: `PostQuery` — client library `query()` methods
|
||||
- Additional operations can be added as client libraries expand their APIs
|
||||
|
||||
**Code sample source:** Maintained in a data directory, not in OpenAPI specs.
|
||||
|
||||
**New directory:** `data/api-code-samples/`
|
||||
|
||||
Structure:
|
||||
```
|
||||
data/api-code-samples/
|
||||
├── write/
|
||||
│ ├── python.md # Python write example
|
||||
│ ├── javascript.md # JavaScript write example
|
||||
│ ├── go.md # Go write example
|
||||
│ ├── java.md # Java write example
|
||||
│ └── csharp.md # C# write example
|
||||
└── query/
|
||||
├── python.md
|
||||
├── javascript.md
|
||||
├── go.md
|
||||
├── java.md
|
||||
└── csharp.md
|
||||
```
|
||||
|
||||
Each file contains just the code sample (no frontmatter), e.g. `data/api-code-samples/write/python.md`:
|
||||
```python
|
||||
from influxdb_client_3 import InfluxDBClient3
|
||||
|
||||
client = InfluxDBClient3(
|
||||
host="{{< influxdb/host >}}",
|
||||
token="DATABASE_TOKEN",
|
||||
database="DATABASE_NAME",
|
||||
)
|
||||
|
||||
client.write(record="home,room=Living\ Room temp=21.1")
|
||||
```
|
||||
|
||||
**Modify:** `layouts/partials/api/code-sample.html`
|
||||
|
||||
Below the curl example, render a tabbed client library section:
|
||||
```html
|
||||
<div class="api-client-library-samples">
|
||||
<div class="api-code-tabs">
|
||||
<button class="api-code-tab is-active" data-lang="python">Python</button>
|
||||
<button class="api-code-tab" data-lang="javascript">JavaScript</button>
|
||||
<button class="api-code-tab" data-lang="go">Go</button>
|
||||
<button class="api-code-tab" data-lang="java">Java</button>
|
||||
<button class="api-code-tab" data-lang="csharp">C#</button>
|
||||
</div>
|
||||
<div class="api-code-tab-content is-active" data-lang="python">
|
||||
<pre><code class="language-python">...</code></pre>
|
||||
</div>
|
||||
<!-- ... other languages ... -->
|
||||
<p class="api-client-library-guide">
|
||||
<a href="/influxdb3/core/write-data/client-libraries/">
|
||||
Full guide: Write data with client libraries
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
```
|
||||
|
||||
**New file:** `assets/js/components/api-code-tabs.ts`
|
||||
|
||||
A simple tab switcher component (lightweight, not the full `tabs-wrapper` shortcode):
|
||||
- Listens for clicks on `.api-code-tab` buttons
|
||||
- Shows/hides corresponding `.api-code-tab-content` panels
|
||||
- Remembers the user's language preference in `localStorage`
|
||||
- Syncs language selection across all client library sections on the page
|
||||
|
||||
**Keeping samples in sync:** The code samples in `data/api-code-samples/` are manually maintained. When client library versions change, the samples need to be updated. This is a documentation maintenance task, same as updating any other code example in the docs. The samples should use the **latest stable** client library version and follow the patterns from the client library reference pages.
|
||||
|
||||
---
|
||||
|
||||
## Files Summary
|
||||
|
||||
### New Files
|
||||
| File | Phase | Purpose |
|
||||
|------|-------|---------|
|
||||
| `layouts/partials/api/code-sample.html` | 1 | Renders inline curl + response schema + client library samples within operations |
|
||||
| `assets/styles/layouts/_api-code-samples.scss` | 1 | Styles for code sample blocks, tabs, and Ask AI links |
|
||||
| `layouts/_default/_markup/render-codeblock.html` | 3 | Hugo render hook adding Ask AI to all code blocks |
|
||||
| `data/api-client-library-links.yml` | 4.1 | Maps operations to client library documentation links |
|
||||
| `data/api-code-samples/write/*.md` | 4.2 | Client library write examples (Python, JS, Go, Java, C#) |
|
||||
| `data/api-code-samples/query/*.md` | 4.2 | Client library query examples (Python, JS, Go, Java, C#) |
|
||||
| `assets/js/components/api-code-tabs.ts` | 4.2 | Lightweight tab switcher for client library code samples |
|
||||
|
||||
### Modified Files
|
||||
| File | Phase | Change |
|
||||
|------|-------|--------|
|
||||
| `api-docs/scripts/generate-openapi-articles.ts` | 1 | Add `curlExample` generation |
|
||||
| `layouts/partials/api/operation.html` | 1 | Include `code-sample.html` partial after responses |
|
||||
| `assets/styles/styles-default.scss` | 1 | Import `_api-code-samples.scss` |
|
||||
| `layouts/partials/api/code-sample.html` | 2, 4.1, 4.2 | Add response body schema, client library links, tabbed code samples |
|
||||
| `assets/js/main.js` | 4.2 | Register `api-code-tabs` component |
|
||||
|
||||
### NOT Modified (kept as-is)
|
||||
| File | Reason |
|
||||
|------|--------|
|
||||
| `layouts/api/list.html` | TOC sidebar stays unchanged |
|
||||
| `assets/js/components/api-toc.ts` | TOC behavior stays unchanged |
|
||||
| `assets/styles/layouts/_api-layout.scss` | Layout widths stay unchanged |
|
||||
|
||||
---
|
||||
|
||||
## Implementation Order
|
||||
|
||||
1. **Phase 1** (MVP): Build script curl generation + inline code sample partial + styles
|
||||
2. **Phase 2**: Successful response body schema (small addition to Phase 1, no build script changes)
|
||||
3. **Phase 3.1**: Ask AI on API code samples (included in Phase 1 HTML)
|
||||
4. **Phase 3.2**: Ask AI on all code blocks site-wide (Hugo render hook)
|
||||
5. **Phase 4.1**: Client library links on relevant operations (data file + partial update)
|
||||
6. **Phase 4.2**: Client library code samples with language tabs (data files + tab component + partial update)
|
||||
|
||||
Phases 1-3.1 can ship together. Phase 3.2 and 4 are independent follow-ups.
|
||||
|
||||
Phase 4.2 starts with write and query operations only, then expands as client libraries add more API coverage.
|
||||
|
||||
---
|
||||
***
|
||||
|
||||
## Verification
|
||||
|
||||
1. **Regenerate articles**: Run `generate-openapi-articles.ts` → verify `curlExample` in frontmatter YAML
|
||||
2. **Build**: `npx hugo --quiet` — verify no template errors
|
||||
3. **Visual**: Dev server → navigate to API tag page → verify each operation has a curl example at the bottom
|
||||
4. **Ask AI**: Click "Ask AI about this example" → verify Kapa opens with pre-populated query
|
||||
5. **Dark mode**: Verify code sample blocks look correct in both themes
|
||||
6. **Responsive**: Verify code samples render well on narrow viewports (no sidebar dependency)
|
||||
7. **E2E test**: Add Cypress test verifying code samples render on API pages
|
||||
1. **Build**: `npx hugo --quiet` — no template errors
|
||||
2. **Visual**: Dev server — navigate to API tag page (e.g., `/influxdb3/core/api/write-data/`) — each operation has a curl example between Request Body and Responses
|
||||
3. **URL replacement**: Set a custom URL in the URL selector — verify it replaces the host in curl examples
|
||||
4. **Ask AI**: Click "Ask AI about this example" — Kapa opens with pre-populated query
|
||||
5. **Related link**: Client library reference link appears at bottom of all InfluxDB 3 API tag pages
|
||||
6. **Cypress**: Add test verifying `.api-code-sample` elements render on tag pages
|
||||
7. **Dark/light mode**: Code block renders correctly in both themes
|
||||
8. **Responsive**: Code sample block handles narrow viewports (horizontal scroll for long curl commands)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,233 @@
|
|||
{{/*
|
||||
API Code Sample
|
||||
|
||||
Renders an inline curl example for an API operation, constructed from the
|
||||
OpenAPI spec at Hugo build time. The existing influxdb-url.js replaces
|
||||
the default host in <pre> elements if the user has a custom URL set.
|
||||
|
||||
Params:
|
||||
- opDef: The operation definition from the parsed spec
|
||||
- operation: Operation metadata from frontmatter (method, path, summary, operationId)
|
||||
- spec: The full OpenAPI spec object for resolving $ref
|
||||
- context: The page context
|
||||
*/}}
|
||||
|
||||
{{ $opDef := .opDef }}
|
||||
{{ $operation := .operation }}
|
||||
{{ $spec := .spec }}
|
||||
{{ $method := upper $operation.method }}
|
||||
{{ $path := $operation.path }}
|
||||
|
||||
{{/* --- Resolve server URL --- */}}
|
||||
{{ $serverUrl := "" }}
|
||||
{{ with index ($spec.servers | default slice) 0 }}
|
||||
{{ $serverUrl = .url | default "" }}
|
||||
{{/* Resolve {variable} placeholders using variable defaults */}}
|
||||
{{ range $varName, $varDef := .variables | default dict }}
|
||||
{{ $placeholder := printf "{%s}" $varName }}
|
||||
{{ $serverUrl = replace $serverUrl $placeholder ($varDef.default | default "") }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if not $serverUrl }}
|
||||
{{ $serverUrl = "http://localhost:8086" }}
|
||||
{{ end }}
|
||||
|
||||
{{/* --- Resolve parameters (handle $ref) --- */}}
|
||||
{{ $params := $opDef.parameters | default slice }}
|
||||
{{ $resolvedParams := slice }}
|
||||
{{ range $params }}
|
||||
{{ $param := . }}
|
||||
{{ if isset . "$ref" }}
|
||||
{{ $refPath := index . "$ref" }}
|
||||
{{ $refParts := split $refPath "/" }}
|
||||
{{ if ge (len $refParts) 4 }}
|
||||
{{ $paramName := index $refParts 3 }}
|
||||
{{ with index $spec.components.parameters $paramName }}
|
||||
{{ $param = . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ $resolvedParams = $resolvedParams | append $param }}
|
||||
{{ end }}
|
||||
|
||||
{{/* --- Build query string from required query parameters --- */}}
|
||||
{{ $queryParts := slice }}
|
||||
{{ range $resolvedParams }}
|
||||
{{ if and (eq .in "query") .required }}
|
||||
{{ $value := "" }}
|
||||
{{ with .schema }}
|
||||
{{ if .example }}
|
||||
{{ $value = .example | string }}
|
||||
{{ else if .default }}
|
||||
{{ $value = .default | string }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if not $value }}
|
||||
{{ $value = .name | upper | replaceRE "[^A-Z0-9]" "_" }}
|
||||
{{ end }}
|
||||
{{ $queryParts = $queryParts | append (printf "%s=%s" .name $value) }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ $fullUrl := printf "%s%s" $serverUrl $path }}
|
||||
{{ if gt (len $queryParts) 0 }}
|
||||
{{ $fullUrl = printf "%s?%s" $fullUrl (delimit $queryParts "&") }}
|
||||
{{ end }}
|
||||
|
||||
{{/* --- Resolve request body (handle $ref) --- */}}
|
||||
{{ $requestBody := $opDef.requestBody | default dict }}
|
||||
{{ if isset $requestBody "$ref" }}
|
||||
{{ $refPath := index $requestBody "$ref" }}
|
||||
{{ $refParts := split $refPath "/" }}
|
||||
{{ if ge (len $refParts) 4 }}
|
||||
{{ $rbName := index $refParts 3 }}
|
||||
{{ with index $spec.components.requestBodies $rbName }}
|
||||
{{ $requestBody = . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* --- Determine content type and body --- */}}
|
||||
{{ $contentType := "" }}
|
||||
{{ $bodyFlag := "" }}
|
||||
{{ $rbContent := $requestBody.content | default dict }}
|
||||
{{ if gt (len $rbContent) 0 }}
|
||||
{{/* Get first content type key */}}
|
||||
{{ range $ct, $_ := $rbContent }}
|
||||
{{ if not $contentType }}
|
||||
{{ $contentType = $ct }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{ $mediaType := index $rbContent $contentType | default dict }}
|
||||
|
||||
{{ if hasPrefix $contentType "text/plain" }}
|
||||
{{/* Line protocol — use first example value or a default sample */}}
|
||||
{{ $lpSample := "measurement,tag=value field=1.0" }}
|
||||
{{ with $mediaType.examples }}
|
||||
{{ range $_, $ex := . }}
|
||||
{{ if not $bodyFlag }}
|
||||
{{ $lpSample = $ex.value | string }}
|
||||
{{/* Take only the first line for single-line display */}}
|
||||
{{ $lines := split $lpSample "\n" }}
|
||||
{{ $lpSample = index $lines 0 }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ $bodyFlag = printf "--data-raw '%s'" $lpSample }}
|
||||
{{ else if hasPrefix $contentType "application/json" }}
|
||||
{{/* JSON — use schema.example, or build from properties */}}
|
||||
{{ with $mediaType.schema }}
|
||||
{{/* Resolve schema $ref */}}
|
||||
{{ $schema := . }}
|
||||
{{ if isset . "$ref" }}
|
||||
{{ $refPath := index . "$ref" }}
|
||||
{{ $refParts := split $refPath "/" }}
|
||||
{{ if ge (len $refParts) 4 }}
|
||||
{{ $schemaName := index $refParts 3 }}
|
||||
{{ with index $spec.components.schemas $schemaName }}
|
||||
{{ $schema = . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ if $schema.example }}
|
||||
{{ $bodyFlag = printf "--data-raw '%s'" (jsonify $schema.example) }}
|
||||
{{ else if $schema.properties }}
|
||||
{{/* Build example JSON from schema properties */}}
|
||||
{{ $bodyObj := dict }}
|
||||
{{ $requiredList := $schema.required | default slice }}
|
||||
{{ range $propName, $propDef := $schema.properties }}
|
||||
{{/* Resolve property $ref */}}
|
||||
{{ $prop := $propDef }}
|
||||
{{ if isset $propDef "$ref" }}
|
||||
{{ $pRefPath := index $propDef "$ref" }}
|
||||
{{ $pRefParts := split $pRefPath "/" }}
|
||||
{{ if ge (len $pRefParts) 4 }}
|
||||
{{ $pSchemaName := index $pRefParts 3 }}
|
||||
{{ with index $spec.components.schemas $pSchemaName }}
|
||||
{{ $prop = . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{/* Use example → default → enum[0] → type placeholder */}}
|
||||
{{ $val := "" }}
|
||||
{{ if ne $prop.example nil }}
|
||||
{{ $val = $prop.example }}
|
||||
{{ else if ne $prop.default nil }}
|
||||
{{ $val = $prop.default }}
|
||||
{{ else if $prop.enum }}
|
||||
{{ $val = index $prop.enum 0 }}
|
||||
{{ else if eq $prop.type "string" }}
|
||||
{{ $val = printf "%s" ($propName | upper) }}
|
||||
{{ else if eq $prop.type "integer" }}
|
||||
{{ $val = 0 }}
|
||||
{{ else if eq $prop.type "number" }}
|
||||
{{ $val = 0 }}
|
||||
{{ else if eq $prop.type "boolean" }}
|
||||
{{ $val = false }}
|
||||
{{ else if eq $prop.type "array" }}
|
||||
{{ if $prop.items }}
|
||||
{{ if eq $prop.items.type "string" }}
|
||||
{{ $val = slice "example" }}
|
||||
{{ else }}
|
||||
{{ $val = slice }}
|
||||
{{ end }}
|
||||
{{ else }}
|
||||
{{ $val = slice }}
|
||||
{{ end }}
|
||||
{{ else if eq $prop.type "object" }}
|
||||
{{ $val = dict }}
|
||||
{{ else }}
|
||||
{{ $val = printf "%s" ($propName | upper) }}
|
||||
{{ end }}
|
||||
{{ $bodyObj = merge $bodyObj (dict $propName $val) }}
|
||||
{{ end }}
|
||||
{{ $bodyFlag = printf "--data-raw '%s'" (jsonify (dict "indent" " ") $bodyObj) }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* --- Assemble curl command --- */}}
|
||||
{{ $lines := slice }}
|
||||
{{ $lines = $lines | append (printf "curl --request %s \\" $method) }}
|
||||
{{ $lines = $lines | append (printf " \"%s\" \\" $fullUrl) }}
|
||||
{{ $lines = $lines | append " --header \"Authorization: Bearer INFLUX_TOKEN\" \\" }}
|
||||
{{ if $contentType }}
|
||||
{{ $lines = $lines | append (printf " --header \"Content-Type: %s\" \\" $contentType) }}
|
||||
{{ end }}
|
||||
{{ if $bodyFlag }}
|
||||
{{/* Last line — no trailing backslash */}}
|
||||
{{ $lines = $lines | append (printf " %s" $bodyFlag) }}
|
||||
{{ else }}
|
||||
{{/* Remove trailing backslash from last header line */}}
|
||||
{{ $lastIdx := sub (len $lines) 1 }}
|
||||
{{ $lastLine := index $lines $lastIdx }}
|
||||
{{ $lastLine = strings.TrimSuffix " \\" $lastLine }}
|
||||
{{ $newLines := slice }}
|
||||
{{ range $i, $line := $lines }}
|
||||
{{ if eq $i $lastIdx }}
|
||||
{{ $newLines = $newLines | append $lastLine }}
|
||||
{{ else }}
|
||||
{{ $newLines = $newLines | append $line }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ $lines = $newLines }}
|
||||
{{ end }}
|
||||
|
||||
{{ $curlCommand := delimit $lines "\n" }}
|
||||
|
||||
{{/* --- Build Ask AI query --- */}}
|
||||
{{ $aiQuery := printf "Explain this %s %s API request and its response: %s" $method $path ($operation.summary | default "") }}
|
||||
|
||||
{{/* --- Render --- */}}
|
||||
<div class="api-code-sample">
|
||||
<h5 class="api-code-sample-header">Example request</h5>
|
||||
<div class="api-code-sample-body">
|
||||
<pre class="api-code-block"><code class="language-sh">{{ $curlCommand }}</code></pre>
|
||||
<a href="#" class="ask-ai-open api-code-ask-ai"
|
||||
data-query="{{ $aiQuery }}">
|
||||
Ask AI about this example
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -54,6 +54,14 @@
|
|||
{{ partial "api/request-body.html" (dict "requestBody" . "spec" $spec) }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Code Sample Section */}}
|
||||
{{ partial "api/code-sample.html" (dict
|
||||
"opDef" $opDef
|
||||
"operation" $operation
|
||||
"spec" $spec
|
||||
"context" .context
|
||||
) }}
|
||||
|
||||
{{/* Responses Section */}}
|
||||
{{ with $opDef.responses }}
|
||||
{{ partial "api/responses.html" (dict "responses" . "spec" $spec) }}
|
||||
|
|
|
|||
|
|
@ -34,17 +34,12 @@
|
|||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Tag description and related links from spec */}}
|
||||
{{/* Tag description from spec */}}
|
||||
{{ $tagDescription := "" }}
|
||||
{{ $tagRelated := slice }}
|
||||
{{ $tagName := .Params.tag | default "" }}
|
||||
{{ range $spec.tags }}
|
||||
{{ if eq .name $tagName }}
|
||||
{{ $tagDescription = .description | default "" }}
|
||||
{{/* Extract x-influxdata-related from the tag */}}
|
||||
{{ with index . "x-influxdata-related" }}
|
||||
{{ $tagRelated = . }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
|
|
@ -69,15 +64,5 @@
|
|||
{{ end }}
|
||||
</section>
|
||||
|
||||
{{/* Related Guides - displayed at bottom like standard page template */}}
|
||||
{{ if gt (len $tagRelated) 0 }}
|
||||
<section class="api-related-guides">
|
||||
<h4 class="api-related-title">Related guides</h4>
|
||||
<ul class="api-related-list">
|
||||
{{ range $tagRelated }}
|
||||
<li><a href="{{ .href }}">{{ .title }}</a></li>
|
||||
{{ end }}
|
||||
</ul>
|
||||
</section>
|
||||
{{ end }}
|
||||
{{/* Related links rendered via frontmatter + article/related.html */}}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,32 +1,25 @@
|
|||
{{ $scratch := newScratch }}
|
||||
{{ if .Params.related }}
|
||||
<div class="related">
|
||||
<h4 id="related">Related</h4>
|
||||
<ul>
|
||||
{{ range .Params.related }}
|
||||
{{ $scratch.Set "relatedItem" . }}
|
||||
{{ $relatedItem := $scratch.Get "relatedItem" }}
|
||||
|
||||
{{ $scratch.Set "title" ""}}
|
||||
{{ if in $relatedItem "," }}
|
||||
{{ $scratch.Set "title" (replaceRE `^.+?, ` "" $relatedItem) }}
|
||||
{{ end }}
|
||||
|
||||
{{ $link := replaceRE `\,\s(.*)$` "" $relatedItem }}
|
||||
{{ $title := $scratch.Get "title" }}
|
||||
|
||||
{{ $isExternal := gt (len (findRE `^http` $relatedItem)) 0 }}
|
||||
{{ if or ($isExternal) (in $relatedItem "/v2/api") (in $relatedItem ",")}}
|
||||
{{ $link := replaceRE `\,\s(.*)$` "" $relatedItem }}
|
||||
{{ $title := replaceRE `^(\S*\,\s)` "" $relatedItem }}
|
||||
{{ $target := cond ($isExternal) "_blank" "" }}
|
||||
<li><a href="{{ $link }}" target="{{ $target }}">{{ $title }}</a></li>
|
||||
|
||||
<!-- Automatically get page title and link from path -->
|
||||
{{/* Handle {title, href} objects */}}
|
||||
{{ if reflect.IsMap . }}
|
||||
<li><a href="{{ .href }}">{{ .title }}</a></li>
|
||||
{{/* Handle string items: "url, title" or plain path */}}
|
||||
{{ else }}
|
||||
{{ $sanitizedPath := replaceRE `\/$` "" (print $relatedItem) }}
|
||||
{{ with $.Page.GetPage $sanitizedPath }}
|
||||
<li><a href="{{ .RelPermalink }}" >{{ .Title | .RenderString }}</a></li>
|
||||
{{ $relatedItem := . }}
|
||||
{{ $isExternal := gt (len (findRE `^http` $relatedItem)) 0 }}
|
||||
{{ if or ($isExternal) (in $relatedItem "/v2/api") (in $relatedItem ",")}}
|
||||
{{ $link := replaceRE `\,\s(.*)$` "" $relatedItem }}
|
||||
{{ $title := replaceRE `^(\S*\,\s)` "" $relatedItem }}
|
||||
{{ $target := cond ($isExternal) "_blank" "" }}
|
||||
<li><a href="{{ $link }}" target="{{ $target }}">{{ $title }}</a></li>
|
||||
{{ else }}
|
||||
{{ $sanitizedPath := replaceRE `\/$` "" (print $relatedItem) }}
|
||||
{{ with $.Page.GetPage $sanitizedPath }}
|
||||
<li><a href="{{ .RelPermalink }}">{{ .Title | .RenderString }}</a></li>
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
|
|
|||
Loading…
Reference in New Issue