@redpanda-data/docs-extensions-and-macros 4.13.1 → 4.13.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/doc-tools-mcp.js +16 -4
- package/bin/doc-tools.js +768 -2089
- package/bin/mcp-tools/generated-docs-review.js +2 -2
- package/bin/mcp-tools/mcp-validation.js +1 -1
- package/bin/mcp-tools/openapi.js +2 -2
- package/bin/mcp-tools/property-docs.js +18 -0
- package/bin/mcp-tools/rpcn-docs.js +28 -3
- package/cli-utils/antora-utils.js +53 -2
- package/cli-utils/dependencies.js +313 -0
- package/cli-utils/diff-utils.js +273 -0
- package/cli-utils/doc-tools-utils.js +54 -0
- package/extensions/algolia-indexer/generate-index.js +134 -102
- package/extensions/algolia-indexer/index.js +70 -38
- package/extensions/collect-bloblang-samples.js +2 -1
- package/extensions/generate-rp-connect-categories.js +125 -67
- package/extensions/generate-rp-connect-info.js +291 -137
- package/macros/rp-connect-components.js +34 -5
- package/package.json +4 -3
- package/tools/add-commercial-names.js +207 -0
- package/tools/bundle-openapi.js +1 -1
- package/tools/generate-cli-docs.js +6 -2
- package/tools/get-console-version.js +5 -0
- package/tools/get-redpanda-version.js +5 -0
- package/tools/property-extractor/compare-properties.js +3 -3
- package/tools/property-extractor/generate-handlebars-docs.js +14 -14
- package/tools/property-extractor/generate-pr-summary.js +46 -0
- package/tools/property-extractor/pr-summary-formatter.js +375 -0
- package/tools/redpanda-connect/README.adoc +403 -38
- package/tools/redpanda-connect/connector-binary-analyzer.js +588 -0
- package/tools/redpanda-connect/generate-rpcn-connector-docs.js +97 -34
- package/tools/redpanda-connect/parse-csv-connectors.js +1 -1
- package/tools/redpanda-connect/pr-summary-formatter.js +663 -0
- package/tools/redpanda-connect/report-delta.js +70 -2
- package/tools/redpanda-connect/rpcn-connector-docs-handler.js +1279 -0
- package/tools/redpanda-connect/templates/connector.hbs +38 -0
- package/tools/redpanda-connect/templates/intro.hbs +0 -20
- package/tools/redpanda-connect/update-nav.js +216 -0
|
@@ -1,186 +1,340 @@
|
|
|
1
1
|
'use strict'
|
|
2
|
-
const fs = require('fs')
|
|
3
|
-
const path = require('path')
|
|
4
|
-
const Papa = require('papaparse')
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const Papa = require('papaparse')
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
|
|
6
|
+
// Default configuration - can be overridden via playbook config
|
|
7
|
+
const DEFAULTS = {
|
|
8
|
+
csvPath: 'internal/plugins/info.csv',
|
|
9
|
+
githubOwner: 'redpanda-data',
|
|
10
|
+
githubRepo: 'connect'
|
|
11
|
+
}
|
|
10
12
|
|
|
11
13
|
module.exports.register = function ({ config }) {
|
|
12
|
-
const logger = this.getLogger('redpanda-connect-info-extension')
|
|
14
|
+
const logger = this.getLogger('redpanda-connect-info-extension')
|
|
15
|
+
const { getAntoraValue } = require('../cli-utils/antora-utils')
|
|
13
16
|
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
// Merge config with defaults
|
|
18
|
+
const {
|
|
19
|
+
csvpath,
|
|
20
|
+
csvPath = DEFAULTS.csvPath,
|
|
21
|
+
githubOwner = DEFAULTS.githubOwner,
|
|
22
|
+
githubRepo = DEFAULTS.githubRepo
|
|
23
|
+
} = config || {}
|
|
24
|
+
|
|
25
|
+
// Use csvpath (legacy) or csvPath
|
|
26
|
+
const localCsvPath = csvpath || null
|
|
27
|
+
|
|
28
|
+
async function loadOctokit () {
|
|
29
|
+
const { Octokit } = await import('@octokit/rest')
|
|
16
30
|
const { getGitHubToken } = require('../cli-utils/github-token')
|
|
17
31
|
const token = getGitHubToken()
|
|
18
|
-
return token ? new Octokit({ auth: token }) : new Octokit()
|
|
32
|
+
return token ? new Octokit({ auth: token }) : new Octokit()
|
|
19
33
|
}
|
|
20
34
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
35
|
+
// Use 'on' and return the promise so Antora waits for async completion
|
|
36
|
+
this.on('contentClassified', ({ contentCatalog }) => {
|
|
37
|
+
return processContent(contentCatalog)
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
async function processContent (contentCatalog) {
|
|
41
|
+
const redpandaConnect = contentCatalog.getComponents().find(component => component.name === 'redpanda-connect')
|
|
42
|
+
const redpandaCloud = contentCatalog.getComponents().find(component => component.name === 'redpanda-cloud')
|
|
43
|
+
const preview = contentCatalog.getComponents().find(component => component.name === 'preview')
|
|
44
|
+
|
|
45
|
+
if (!redpandaConnect) {
|
|
46
|
+
logger.warn('redpanda-connect component not found, skipping CSV enrichment')
|
|
47
|
+
return
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const pages = contentCatalog.getPages()
|
|
27
51
|
|
|
28
52
|
try {
|
|
29
|
-
//
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
53
|
+
// Get the Connect version from antora.yml
|
|
54
|
+
const connectVersion = getAntoraValue('asciidoc.attributes.latest-connect-version')
|
|
55
|
+
|
|
56
|
+
// Fetch CSV data (from local file first, then GitHub as fallback)
|
|
57
|
+
const csvData = await fetchCSV(localCsvPath, connectVersion, logger)
|
|
58
|
+
const parsedData = Papa.parse(csvData, { header: true, skipEmptyLines: true })
|
|
59
|
+
const enrichedData = translateCsvData(parsedData, pages, logger)
|
|
60
|
+
parsedData.data = enrichedData
|
|
61
|
+
|
|
62
|
+
// Set csvData on all relevant components
|
|
63
|
+
const componentsToEnrich = [redpandaConnect, redpandaCloud, preview].filter(Boolean)
|
|
64
|
+
for (const component of componentsToEnrich) {
|
|
65
|
+
if (component.latest?.asciidoc?.attributes) {
|
|
66
|
+
component.latest.asciidoc.attributes.csvData = parsedData
|
|
67
|
+
}
|
|
40
68
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
69
|
+
|
|
70
|
+
// Enrich component pages with commercial names from CSV + AsciiDoc
|
|
71
|
+
const commercialNamesMap = enrichPagesWithCommercialNames(pages, parsedData, logger)
|
|
72
|
+
|
|
73
|
+
// Convert Map to plain object for serialization and macro access
|
|
74
|
+
const commercialNamesObj = {}
|
|
75
|
+
commercialNamesMap.forEach((names, connector) => {
|
|
76
|
+
commercialNamesObj[connector] = Array.from(names)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// Make commercial names available to macros
|
|
80
|
+
for (const component of componentsToEnrich) {
|
|
81
|
+
if (component.latest?.asciidoc?.attributes) {
|
|
82
|
+
component.latest.asciidoc.attributes.commercialNamesMap = commercialNamesObj
|
|
83
|
+
}
|
|
44
84
|
}
|
|
45
85
|
|
|
86
|
+
logger.info(`Successfully processed ${parsedData.data.length} connectors from CSV`)
|
|
46
87
|
} catch (error) {
|
|
47
|
-
logger.error(
|
|
48
|
-
logger.error(error.stack)
|
|
88
|
+
logger.error(`Error fetching or parsing CSV data: ${error.message}`)
|
|
89
|
+
logger.error(error.stack)
|
|
90
|
+
// Don't throw - allow build to continue with degraded functionality
|
|
49
91
|
}
|
|
50
|
-
}
|
|
92
|
+
}
|
|
51
93
|
|
|
52
|
-
//
|
|
53
|
-
async function fetchCSV(
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
94
|
+
// Fetch CSV from GitHub or local file (local file for testing/override only)
|
|
95
|
+
async function fetchCSV (localPath, connectVersion, logger) {
|
|
96
|
+
// Priority 1: Use explicitly provided CSV path (for testing/override)
|
|
97
|
+
if (localPath && fs.existsSync(localPath)) {
|
|
98
|
+
if (path.extname(localPath).toLowerCase() !== '.csv') {
|
|
99
|
+
throw new Error(`Invalid file type: ${localPath}. Expected a CSV file.`)
|
|
57
100
|
}
|
|
58
|
-
logger.info(`Loading CSV data from local file: ${
|
|
59
|
-
return fs.readFileSync(
|
|
60
|
-
} else {
|
|
61
|
-
logger.info('Local CSV file not found. Fetching from GitHub...');
|
|
62
|
-
return await fetchCsvFromGitHub();
|
|
101
|
+
logger.info(`Loading CSV data from local file: ${localPath}`)
|
|
102
|
+
return fs.readFileSync(localPath, 'utf8')
|
|
63
103
|
}
|
|
104
|
+
|
|
105
|
+
// Priority 2: Fetch from GitHub using the version tag
|
|
106
|
+
logger.info(`Fetching CSV from GitHub (version: ${connectVersion || 'main'})...`)
|
|
107
|
+
return fetchCsvFromGitHub(connectVersion)
|
|
64
108
|
}
|
|
65
109
|
|
|
66
110
|
// Fetch CSV data from GitHub
|
|
67
|
-
async function fetchCsvFromGitHub() {
|
|
68
|
-
const octokit = await loadOctokit()
|
|
111
|
+
async function fetchCsvFromGitHub (connectVersion) {
|
|
112
|
+
const octokit = await loadOctokit()
|
|
113
|
+
// Normalize version: trim whitespace and remove leading 'v' if present
|
|
114
|
+
const normalizedVersion = connectVersion ? connectVersion.trim().replace(/^v/, '') : ''
|
|
115
|
+
// Use version tag if valid, otherwise fallback to main branch
|
|
116
|
+
const ref = normalizedVersion ? `v${normalizedVersion}` : 'main'
|
|
117
|
+
|
|
69
118
|
try {
|
|
70
119
|
const { data: fileContent } = await octokit.rest.repos.getContent({
|
|
71
|
-
owner:
|
|
72
|
-
repo:
|
|
73
|
-
path:
|
|
74
|
-
ref:
|
|
75
|
-
})
|
|
76
|
-
return Buffer.from(fileContent.content, 'base64').toString('utf8')
|
|
120
|
+
owner: githubOwner,
|
|
121
|
+
repo: githubRepo,
|
|
122
|
+
path: csvPath,
|
|
123
|
+
ref: ref
|
|
124
|
+
})
|
|
125
|
+
return Buffer.from(fileContent.content, 'base64').toString('utf8')
|
|
77
126
|
} catch (error) {
|
|
78
|
-
|
|
79
|
-
|
|
127
|
+
logger.error(`Error fetching Redpanda Connect catalog from GitHub (ref: ${ref}): ${error.message}`)
|
|
128
|
+
throw error
|
|
80
129
|
}
|
|
81
130
|
}
|
|
82
131
|
|
|
83
132
|
/**
|
|
84
133
|
* Transforms and enriches parsed CSV connector data with normalized fields and documentation URLs.
|
|
85
|
-
*
|
|
86
|
-
* Each row is trimmed, mapped to expected output fields, and enriched with documentation URLs for Redpanda Connect and Cloud components if available. The `support` field is normalized, and licensing information is derived. Logs a warning if documentation URLs are missing for non-deprecated, non-SQL driver connectors that indicate cloud support.
|
|
87
|
-
*
|
|
88
|
-
* @param {object} parsedData - Parsed CSV data containing connector rows.
|
|
89
|
-
* @param {array} pages - Array of page objects used to resolve documentation URLs.
|
|
90
|
-
* @param {object} logger - Logger instance for warning about missing documentation.
|
|
91
|
-
* @returns {array} Array of enriched connector objects with normalized fields and URLs.
|
|
134
|
+
* Uses O(n) lookup maps for efficient page matching.
|
|
92
135
|
*/
|
|
93
|
-
function translateCsvData(parsedData, pages, logger) {
|
|
136
|
+
function translateCsvData (parsedData, pages, logger) {
|
|
137
|
+
// Build lookup maps once for O(1) access - much faster than O(n) iteration per row
|
|
138
|
+
const connectPages = new Map()
|
|
139
|
+
const cloudPages = new Map()
|
|
140
|
+
|
|
141
|
+
for (const file of pages) {
|
|
142
|
+
const { component } = file.src
|
|
143
|
+
const stem = file.src.stem
|
|
144
|
+
const filePath = file.path
|
|
145
|
+
|
|
146
|
+
if (component === 'redpanda-connect') {
|
|
147
|
+
// Store by stem, but only for connector doc paths
|
|
148
|
+
if (isConnectorDocPath(filePath, file)) {
|
|
149
|
+
const type = extractTypeFromPath(filePath)
|
|
150
|
+
if (type) {
|
|
151
|
+
const key = `${stem}:${type}`
|
|
152
|
+
connectPages.set(key, file)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
} else if (component === 'redpanda-cloud') {
|
|
156
|
+
// Cloud docs have a specific path pattern
|
|
157
|
+
const cloudMatch = filePath.match(/connect\/components\/([^/]+)s\/([^/]+)\.adoc$/)
|
|
158
|
+
if (cloudMatch) {
|
|
159
|
+
const [, type, name] = cloudMatch
|
|
160
|
+
const key = `${name}:${type}`
|
|
161
|
+
cloudPages.set(key, file)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function isConnectorDocPath (filePath) {
|
|
167
|
+
const dirsToCheck = [
|
|
168
|
+
'/pages/inputs/',
|
|
169
|
+
'/pages/outputs/',
|
|
170
|
+
'/pages/processors/',
|
|
171
|
+
'/pages/caches/',
|
|
172
|
+
'/pages/rate_limits/',
|
|
173
|
+
'/pages/buffers/',
|
|
174
|
+
'/pages/metrics/',
|
|
175
|
+
'/pages/tracers/',
|
|
176
|
+
'/pages/scanners/',
|
|
177
|
+
'/partials/components/'
|
|
178
|
+
]
|
|
179
|
+
return dirsToCheck.some(dir => filePath.includes(dir))
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function extractTypeFromPath (filePath) {
|
|
183
|
+
const typeMatch = filePath.match(/\/(inputs|outputs|processors|caches|rate_limits|buffers|metrics|tracers|scanners)\//)
|
|
184
|
+
if (typeMatch) {
|
|
185
|
+
// Convert plural to singular
|
|
186
|
+
return typeMatch[1].replace(/s$/, '').replace('rate_limit', 'rate_limit')
|
|
187
|
+
}
|
|
188
|
+
return null
|
|
189
|
+
}
|
|
190
|
+
|
|
94
191
|
return parsedData.data.map(row => {
|
|
95
192
|
// Create a new object with trimmed keys and values
|
|
96
193
|
const trimmedRow = Object.fromEntries(
|
|
97
|
-
Object.entries(row).map(([key, value]) => [key.trim(), value.trim()])
|
|
98
|
-
)
|
|
194
|
+
Object.entries(row).map(([key, value]) => [key.trim(), (value || '').trim()])
|
|
195
|
+
)
|
|
99
196
|
|
|
100
197
|
// Map fields from the trimmed row to the desired output
|
|
101
|
-
const connector = trimmedRow.name
|
|
102
|
-
const type = trimmedRow.type
|
|
103
|
-
const
|
|
104
|
-
const
|
|
105
|
-
const deprecated = trimmedRow.deprecated.toLowerCase() === 'y' ? 'y' : 'n'
|
|
106
|
-
const
|
|
107
|
-
const
|
|
198
|
+
const connector = trimmedRow.name
|
|
199
|
+
const type = trimmedRow.type
|
|
200
|
+
const commercialName = trimmedRow.commercial_name
|
|
201
|
+
const availableConnectVersion = trimmedRow.version
|
|
202
|
+
const deprecated = (trimmedRow.deprecated || '').toLowerCase() === 'y' ? 'y' : 'n'
|
|
203
|
+
const isCloudSupported = (trimmedRow.cloud || '').toLowerCase() === 'y' ? 'y' : 'n'
|
|
204
|
+
const cloudAi = (trimmedRow.cloud_with_gpu || '').toLowerCase() === 'y' ? 'y' : 'n'
|
|
205
|
+
|
|
108
206
|
// Handle enterprise to certified conversion and set enterprise license flag
|
|
109
|
-
const originalSupport = trimmedRow.support.toLowerCase()
|
|
110
|
-
const
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
// Redpanda Connect and Cloud enrichment URLs
|
|
114
|
-
let redpandaConnectUrl = '';
|
|
115
|
-
let redpandaCloudUrl = '';
|
|
116
|
-
|
|
117
|
-
function isConnectorDocPath (filePath, type) {
|
|
118
|
-
const dirsToCheck = [
|
|
119
|
-
`pages/${type}s/`,
|
|
120
|
-
`partials/components/${type}s/`
|
|
121
|
-
];
|
|
122
|
-
return dirsToCheck.some(dir => filePath.includes(dir));
|
|
123
|
-
}
|
|
207
|
+
const originalSupport = (trimmedRow.support || '').toLowerCase()
|
|
208
|
+
const supportLevel = originalSupport === 'enterprise' ? 'certified' : originalSupport
|
|
209
|
+
const isLicensed = originalSupport === 'enterprise' ? 'Yes' : 'No'
|
|
124
210
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
);
|
|
130
|
-
}
|
|
211
|
+
// O(1) lookup for URLs
|
|
212
|
+
const lookupKey = `${connector}:${type}`
|
|
213
|
+
const connectPage = connectPages.get(lookupKey)
|
|
214
|
+
const cloudPage = cloudPages.get(lookupKey)
|
|
131
215
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
const { component } = file.src; // such as 'redpanda-connect'
|
|
135
|
-
const { path: filePath } = file; // such as modules/.../pages/sinks/foo.adoc
|
|
136
|
-
|
|
137
|
-
if (
|
|
138
|
-
component === 'redpanda-connect' &&
|
|
139
|
-
filePath.endsWith(`/${connector}.adoc`) &&
|
|
140
|
-
isConnectorDocPath(filePath, type)
|
|
141
|
-
) {
|
|
142
|
-
redpandaConnectUrl = file.pub.url;
|
|
143
|
-
}
|
|
216
|
+
const redpandaConnectUrl = connectPage?.pub?.url || ''
|
|
217
|
+
const redpandaCloudUrl = cloudPage?.pub?.url || ''
|
|
144
218
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
219
|
+
// Warn about missing docs (but not for deprecated or SQL drivers)
|
|
220
|
+
if (deprecated !== 'y' && !connector.includes('sql_driver')) {
|
|
221
|
+
if (!redpandaConnectUrl) {
|
|
222
|
+
logger.warn(`Self-Managed docs missing for: ${connector} of type: ${type}`)
|
|
223
|
+
}
|
|
224
|
+
if (isCloudSupported === 'y' && !redpandaCloudUrl && redpandaConnectUrl) {
|
|
225
|
+
logger.warn(`Cloud docs missing for: ${connector} of type: ${type}`)
|
|
150
226
|
}
|
|
151
227
|
}
|
|
152
228
|
|
|
153
|
-
if (
|
|
154
|
-
deprecated !== 'y' &&
|
|
155
|
-
!connector.includes('sql_driver') &&
|
|
156
|
-
!redpandaConnectUrl &&
|
|
157
|
-
!(is_cloud_supported === 'y' && redpandaCloudUrl)
|
|
158
|
-
) {
|
|
159
|
-
logger.warn(`Self-Managed docs missing for: ${connector} of type: ${type}`);
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
if (
|
|
163
|
-
is_cloud_supported === 'y' &&
|
|
164
|
-
!redpandaCloudUrl &&
|
|
165
|
-
redpandaConnectUrl
|
|
166
|
-
) {
|
|
167
|
-
logger.warn(`Cloud docs missing for: ${connector} of type: ${type}`);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Return the translated and enriched row
|
|
171
229
|
return {
|
|
172
230
|
connector,
|
|
173
231
|
type,
|
|
174
|
-
commercial_name,
|
|
175
|
-
available_connect_version,
|
|
176
|
-
support_level,
|
|
232
|
+
commercial_name: commercialName,
|
|
233
|
+
available_connect_version: availableConnectVersion,
|
|
234
|
+
support_level: supportLevel,
|
|
177
235
|
deprecated,
|
|
178
|
-
is_cloud_supported,
|
|
179
|
-
cloud_ai,
|
|
180
|
-
is_licensed,
|
|
236
|
+
is_cloud_supported: isCloudSupported,
|
|
237
|
+
cloud_ai: cloudAi,
|
|
238
|
+
is_licensed: isLicensed,
|
|
181
239
|
redpandaConnectUrl,
|
|
182
|
-
redpandaCloudUrl
|
|
183
|
-
}
|
|
184
|
-
})
|
|
240
|
+
redpandaCloudUrl
|
|
241
|
+
}
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Enriches component pages with commercial names from CSV data and existing AsciiDoc attributes.
|
|
247
|
+
*/
|
|
248
|
+
function enrichPagesWithCommercialNames (pages, parsedData, logger) {
|
|
249
|
+
// Build a lookup map: connector name -> Set of commercial names from CSV
|
|
250
|
+
const csvCommercialNames = new Map()
|
|
251
|
+
|
|
252
|
+
for (const row of parsedData.data) {
|
|
253
|
+
const { connector, commercial_name: commercialName } = row
|
|
254
|
+
if (!connector || !commercialName) continue
|
|
255
|
+
|
|
256
|
+
// Skip N/A and empty values
|
|
257
|
+
const trimmedName = commercialName.trim()
|
|
258
|
+
if (trimmedName.toLowerCase() === 'n/a' || trimmedName === '') continue
|
|
259
|
+
|
|
260
|
+
if (!csvCommercialNames.has(connector)) {
|
|
261
|
+
csvCommercialNames.set(connector, new Set())
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// Add the commercial name if it's different from the connector name
|
|
265
|
+
if (trimmedName.toLowerCase() !== connector.toLowerCase()) {
|
|
266
|
+
csvCommercialNames.get(connector).add(trimmedName)
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Enrich each component page with combined commercial names
|
|
271
|
+
let enrichedCount = 0
|
|
272
|
+
|
|
273
|
+
for (const page of pages) {
|
|
274
|
+
const { component, relative, module: moduleName } = page.src
|
|
275
|
+
|
|
276
|
+
// Only process Redpanda Connect and Cloud component pages
|
|
277
|
+
if (component !== 'redpanda-connect' && component !== 'redpanda-cloud') continue
|
|
278
|
+
|
|
279
|
+
// Match component documentation pages:
|
|
280
|
+
// 1. Cloud-style paths: connect/components/processors/archive.adoc
|
|
281
|
+
// 2. Connect module-based paths: module=components, relative=processors/archive.adoc
|
|
282
|
+
const isComponentsModule = moduleName === 'components'
|
|
283
|
+
const hasComponentsInPath = relative.includes('/components/')
|
|
284
|
+
|
|
285
|
+
if (!isComponentsModule && !hasComponentsInPath) continue
|
|
286
|
+
|
|
287
|
+
// Extract connector name from path
|
|
288
|
+
let connectorMatch
|
|
289
|
+
if (hasComponentsInPath) {
|
|
290
|
+
connectorMatch = relative.match(/\/components\/[^/]+\/([^/]+)\.adoc$/)
|
|
291
|
+
} else if (isComponentsModule) {
|
|
292
|
+
connectorMatch = relative.match(/^[^/]+\/([^/]+)\.adoc$/)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (!connectorMatch) continue
|
|
296
|
+
|
|
297
|
+
const connectorName = connectorMatch[1]
|
|
298
|
+
const csvNames = csvCommercialNames.get(connectorName) || new Set()
|
|
299
|
+
|
|
300
|
+
// Get existing commercial names from AsciiDoc page attribute
|
|
301
|
+
let existingNames = []
|
|
302
|
+
const existingAttr = page.asciidoc?.attributes?.['page-commercial-names']
|
|
303
|
+
|
|
304
|
+
if (existingAttr) {
|
|
305
|
+
existingNames = existingAttr.split(',').map(n => n.trim()).filter(n => n)
|
|
306
|
+
} else if (page.contents) {
|
|
307
|
+
// Fallback: parse from file contents if attribute not yet available
|
|
308
|
+
// Note: This regex handles single-line attributes only
|
|
309
|
+
const fileContents = page.contents.toString('utf8')
|
|
310
|
+
const attrMatch = fileContents.match(/:page-commercial-names:\s*(.+)/)
|
|
311
|
+
if (attrMatch) {
|
|
312
|
+
existingNames = attrMatch[1].split(',').map(n => n.trim()).filter(n => n)
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Combine CSV names and existing names, deduplicate
|
|
317
|
+
const allNames = new Set([...csvNames, ...existingNames])
|
|
318
|
+
|
|
319
|
+
if (allNames.size > 0) {
|
|
320
|
+
// Ensure attributes object exists
|
|
321
|
+
if (!page.asciidoc) page.asciidoc = {}
|
|
322
|
+
if (!page.asciidoc.attributes) page.asciidoc.attributes = {}
|
|
323
|
+
|
|
324
|
+
// Set the combined commercial names as a comma-separated list
|
|
325
|
+
const commercialNamesList = Array.from(allNames).join(', ')
|
|
326
|
+
page.asciidoc.attributes['page-commercial-names'] = commercialNamesList
|
|
327
|
+
enrichedCount++
|
|
328
|
+
|
|
329
|
+
// Update the mapping with the enriched names
|
|
330
|
+
csvCommercialNames.set(connectorName, allNames)
|
|
331
|
+
|
|
332
|
+
logger.debug(`Added commercial names to ${connectorName}: ${commercialNamesList}`)
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
logger.info(`Enriched ${enrichedCount} component pages with commercial names`)
|
|
337
|
+
|
|
338
|
+
return csvCommercialNames
|
|
185
339
|
}
|
|
186
|
-
}
|
|
340
|
+
}
|
|
@@ -350,7 +350,7 @@ module.exports.register = function (registry, context) {
|
|
|
350
350
|
* - Enterprise licensing information
|
|
351
351
|
* - Cloud support status (Yes/No with a link if applicable)
|
|
352
352
|
*/
|
|
353
|
-
function generateConnectorsHTMLTable(connectors, sqlDrivers, isCloud, showAllInfo) {
|
|
353
|
+
function generateConnectorsHTMLTable(connectors, sqlDrivers, isCloud, showAllInfo, commercialNamesMap = {}) {
|
|
354
354
|
return Object.entries(connectors)
|
|
355
355
|
.filter(([_, details]) => {
|
|
356
356
|
// If isCloud is true, filter out rows that do not support cloud
|
|
@@ -468,12 +468,38 @@ module.exports.register = function (registry, context) {
|
|
|
468
468
|
|
|
469
469
|
const firstUrl = getFirstUrlFromTypesArray(Array.from(types.entries()), isCloud);
|
|
470
470
|
|
|
471
|
+
// Collect all unique commercial names for search
|
|
472
|
+
// First try to get enriched names from the map, then fall back to CSV
|
|
473
|
+
const allCommercialNames = new Set();
|
|
474
|
+
if (commercialNamesMap[connector]) {
|
|
475
|
+
// Use enriched names from the map (includes CSV + AsciiDoc)
|
|
476
|
+
commercialNamesMap[connector].forEach(name => {
|
|
477
|
+
if (name.toLowerCase() !== connector.toLowerCase() &&
|
|
478
|
+
name.toLowerCase() !== 'n/a') {
|
|
479
|
+
allCommercialNames.add(name);
|
|
480
|
+
}
|
|
481
|
+
});
|
|
482
|
+
} else {
|
|
483
|
+
// Fallback to CSV-only names
|
|
484
|
+
Array.from(types.entries()).forEach(([type, commercialNames]) => {
|
|
485
|
+
Object.keys(commercialNames).forEach(commercialName => {
|
|
486
|
+
if (commercialName.toLowerCase() !== connector.toLowerCase() &&
|
|
487
|
+
commercialName.toLowerCase() !== 'n/a') {
|
|
488
|
+
allCommercialNames.add(commercialName);
|
|
489
|
+
}
|
|
490
|
+
});
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
const commercialNamesText = allCommercialNames.size > 0
|
|
494
|
+
? `<span class="search-terms" style="position: absolute; left: -9999px;">${Array.from(allCommercialNames).join(' ')}</span>`
|
|
495
|
+
: '';
|
|
496
|
+
|
|
471
497
|
// Logic for showAllInfo = true and isCloud = false
|
|
472
498
|
if (showAllInfo && !isCloud) {
|
|
473
499
|
return `
|
|
474
500
|
<tr id="row-${id}">
|
|
475
501
|
<td class="tableblock halign-left valign-top" id="componentName-${id}">
|
|
476
|
-
<p class="tableblock"><a href="${firstUrl}"><code>${connector}</code></a
|
|
502
|
+
<p class="tableblock"><a href="${firstUrl}"><code>${connector}</code></a>${commercialNamesText}</p>
|
|
477
503
|
</td>
|
|
478
504
|
<td class="tableblock halign-left valign-top" id="componentType-${id}">
|
|
479
505
|
<p class="tableblock">${typesArray}</p> <!-- Display types linked to Connect URL only -->
|
|
@@ -494,7 +520,7 @@ module.exports.register = function (registry, context) {
|
|
|
494
520
|
return `
|
|
495
521
|
<tr id="row-${id}">
|
|
496
522
|
<td class="tableblock halign-left valign-top" id="componentName-${id}">
|
|
497
|
-
<p class="tableblock"><a href="${firstUrl}"><code>${connector}</code></a
|
|
523
|
+
<p class="tableblock"><a href="${firstUrl}"><code>${connector}</code></a>${commercialNamesText}</p>
|
|
498
524
|
</td>
|
|
499
525
|
<td class="tableblock halign-left valign-top" id="componentType-${id}">
|
|
500
526
|
${typesArray} <!-- Display bulleted list for cloud types if commercial name differs -->
|
|
@@ -505,7 +531,7 @@ module.exports.register = function (registry, context) {
|
|
|
505
531
|
return `
|
|
506
532
|
<tr id="row-${id}">
|
|
507
533
|
<td class="tableblock halign-left valign-top" id="componentName-${id}">
|
|
508
|
-
<p class="tableblock"><a href="${firstUrl}"><code>${connector}</code></a
|
|
534
|
+
<p class="tableblock"><a href="${firstUrl}"><code>${connector}</code></a>${commercialNamesText}</p>
|
|
509
535
|
</td>
|
|
510
536
|
<td class="tableblock halign-left valign-top" id="componentType-${id}">
|
|
511
537
|
<p class="tableblock">${typesArray}</p> <!-- Display types without commercial names -->
|
|
@@ -576,6 +602,9 @@ module.exports.register = function (registry, context) {
|
|
|
576
602
|
const csvData = context.config?.attributes?.csvData || null;
|
|
577
603
|
if (!csvData) return console.error(`CSV data is not available for ${parent.getDocument().getAttributes()['page-relative-src-path']}. Make sure your playbook includes the generate-rp-connect-info extension.`)
|
|
578
604
|
|
|
605
|
+
// Get the enriched commercial names map (includes CSV + AsciiDoc names)
|
|
606
|
+
const commercialNamesMap = context.config?.attributes?.commercialNamesMap || {};
|
|
607
|
+
|
|
579
608
|
const sqlDriversData = processSqlDrivers(csvData);
|
|
580
609
|
|
|
581
610
|
const types = new Set();
|
|
@@ -695,7 +724,7 @@ module.exports.register = function (registry, context) {
|
|
|
695
724
|
</tr>
|
|
696
725
|
</thead>
|
|
697
726
|
<tbody>
|
|
698
|
-
${generateConnectorsHTMLTable(processConnectors(csvData), sqlDriversData, isCloud, showAllInfo)}
|
|
727
|
+
${generateConnectorsHTMLTable(processConnectors(csvData), sqlDriversData, isCloud, showAllInfo, commercialNamesMap)}
|
|
699
728
|
</tbody>
|
|
700
729
|
</table>
|
|
701
730
|
<script>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@redpanda-data/docs-extensions-and-macros",
|
|
3
|
-
"version": "4.13.
|
|
3
|
+
"version": "4.13.3",
|
|
4
4
|
"description": "Antora extensions and macros developed for Redpanda documentation.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"antora",
|
|
@@ -28,7 +28,8 @@
|
|
|
28
28
|
"test:mcp:integration": "jest __tests__/mcp/integration.test.js",
|
|
29
29
|
"test:mcp:contract": "jest __tests__/mcp/cli-contract.test.js",
|
|
30
30
|
"test:python": "./__tests__/tools/property-extractor/setup-and-test.sh",
|
|
31
|
-
"test:
|
|
31
|
+
"test:cgo": "node __tests__/tools/cgo-detection.test.js",
|
|
32
|
+
"test:all": "npm run test && npm run test:python && npm run test:cgo",
|
|
32
33
|
"generate:cli-docs": "node tools/generate-cli-docs.js",
|
|
33
34
|
"bundle:admin": "doc-tools generate bundle-openapi --surface admin",
|
|
34
35
|
"bundle:connect": "doc-tools generate bundle-openapi --surface connect",
|
|
@@ -95,7 +96,7 @@
|
|
|
95
96
|
"@modelcontextprotocol/sdk": "^1.0.4",
|
|
96
97
|
"@octokit/core": "^6.1.2",
|
|
97
98
|
"@octokit/plugin-retry": "^7.1.1",
|
|
98
|
-
"@octokit/rest": "^21.
|
|
99
|
+
"@octokit/rest": "^21.1.1",
|
|
99
100
|
"@redocly/cli": "^2.2.0",
|
|
100
101
|
"ajv": "^8.12.0",
|
|
101
102
|
"algoliasearch": "^4.17.0",
|