@redpanda-data/docs-extensions-and-macros 4.15.7 → 4.15.9
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/package.json +1 -1
- package/tools/property-extractor/property_extractor.py +20 -4
- package/tools/redpanda-connect/connector-binary-analyzer.js +2 -0
- package/tools/redpanda-connect/generate-rpcn-connector-docs.js +87 -0
- package/tools/redpanda-connect/helpers/bloblangExample.js +23 -3
- package/tools/redpanda-connect/helpers/ensurePeriod.js +27 -0
- package/tools/redpanda-connect/helpers/hasOptionalParams.js +7 -0
- package/tools/redpanda-connect/helpers/index.js +3 -0
- package/tools/redpanda-connect/helpers/toSentenceCase.js +69 -0
- package/tools/redpanda-connect/pr-summary-formatter.js +304 -26
- package/tools/redpanda-connect/report-delta.js +137 -7
- package/tools/redpanda-connect/rpcn-connector-docs-handler.js +86 -54
- package/tools/redpanda-connect/templates/bloblang-function.hbs +28 -2
- package/tools/redpanda-connect/templates/bloblang-functions-overview.hbs +29 -0
- package/tools/redpanda-connect/templates/bloblang-methods-overview.hbs +39 -0
- package/tools/redpanda-connect/templates/connector.hbs +1 -2
- package/tools/redpanda-connect/templates/intro.hbs +1 -1
package/package.json
CHANGED
|
@@ -991,6 +991,14 @@ def _apply_override_to_existing_property(property_dict, override, overrides_file
|
|
|
991
991
|
if "category" in override:
|
|
992
992
|
property_dict["category"] = override["category"]
|
|
993
993
|
|
|
994
|
+
# Apply accepted_values override - replaces enum field to filter displayed values
|
|
995
|
+
# Use case: Exclude internal-only enum values (e.g., greedy mode for leader_balancer_mode)
|
|
996
|
+
if "accepted_values" in override:
|
|
997
|
+
if isinstance(override["accepted_values"], list):
|
|
998
|
+
property_dict["enum"] = override["accepted_values"]
|
|
999
|
+
else:
|
|
1000
|
+
logger.warning(f"accepted_values for property must be an array")
|
|
1001
|
+
|
|
994
1002
|
|
|
995
1003
|
def _create_property_from_override(prop_name, override, overrides_file_path):
|
|
996
1004
|
"""Create a new property from override specification."""
|
|
@@ -1025,9 +1033,10 @@ def _create_property_from_override(prop_name, override, overrides_file_path):
|
|
|
1025
1033
|
|
|
1026
1034
|
# Add any other custom fields from override
|
|
1027
1035
|
for key, value in override.items():
|
|
1028
|
-
if key not in ["description", "type", "default", "config_scope", "version",
|
|
1029
|
-
"example", "example_file", "example_yaml", "related_topics",
|
|
1030
|
-
"is_deprecated", "visibility"
|
|
1036
|
+
if key not in ["description", "type", "default", "config_scope", "version",
|
|
1037
|
+
"example", "example_file", "example_yaml", "related_topics",
|
|
1038
|
+
"is_deprecated", "visibility", "exclude_from_docs", "category",
|
|
1039
|
+
"accepted_values", "_comment"]:
|
|
1031
1040
|
new_property[key] = value
|
|
1032
1041
|
|
|
1033
1042
|
# Add exclude_from_docs if specified
|
|
@@ -1037,7 +1046,14 @@ def _create_property_from_override(prop_name, override, overrides_file_path):
|
|
|
1037
1046
|
# Add category if specified
|
|
1038
1047
|
if "category" in override:
|
|
1039
1048
|
new_property["category"] = override["category"]
|
|
1040
|
-
|
|
1049
|
+
|
|
1050
|
+
# Add accepted_values as enum field if specified
|
|
1051
|
+
if "accepted_values" in override:
|
|
1052
|
+
if isinstance(override["accepted_values"], list):
|
|
1053
|
+
new_property["enum"] = override["accepted_values"]
|
|
1054
|
+
else:
|
|
1055
|
+
logger.warning(f"accepted_values for property '{prop_name}' must be an array")
|
|
1056
|
+
|
|
1041
1057
|
return new_property
|
|
1042
1058
|
|
|
1043
1059
|
|
|
@@ -247,6 +247,7 @@ function getConnectorList(binaryPath) {
|
|
|
247
247
|
|
|
248
248
|
let result;
|
|
249
249
|
if (needsDocker) {
|
|
250
|
+
console.log(` Using Docker to run Linux binary on ${os.platform()}`);
|
|
250
251
|
// Use Docker to run Linux binaries on macOS/Windows
|
|
251
252
|
const binaryDir = path.dirname(binaryPath);
|
|
252
253
|
const binaryFile = path.basename(binaryPath);
|
|
@@ -268,6 +269,7 @@ function getConnectorList(binaryPath) {
|
|
|
268
269
|
maxBuffer: 10 * 1024 * 1024 // 10MB buffer
|
|
269
270
|
});
|
|
270
271
|
} else {
|
|
272
|
+
console.log(` Running natively on ${os.platform()}`);
|
|
271
273
|
// Run natively
|
|
272
274
|
result = spawnSync(binaryPath, ['list', '--format', 'json-full'], {
|
|
273
275
|
stdio: ['ignore', 'pipe', 'ignore'],
|
|
@@ -472,6 +472,28 @@ async function generateRpcnConnectorDocs(options) {
|
|
|
472
472
|
if (!Array.isArray(items)) continue;
|
|
473
473
|
const outRoot = path.join(outputRoot, folder);
|
|
474
474
|
fs.mkdirSync(outRoot, { recursive: true });
|
|
475
|
+
|
|
476
|
+
// Get current item names
|
|
477
|
+
const currentNames = new Set(items.filter(fn => fn.name).map(fn => fn.name));
|
|
478
|
+
|
|
479
|
+
// Delete partials that no longer exist in the data
|
|
480
|
+
if (fs.existsSync(outRoot)) {
|
|
481
|
+
const existingFiles = fs.readdirSync(outRoot).filter(f => f.endsWith('.adoc'));
|
|
482
|
+
for (const file of existingFiles) {
|
|
483
|
+
const name = file.replace('.adoc', '');
|
|
484
|
+
// Skip hand-authored files (by convention, start with underscore)
|
|
485
|
+
if (name.startsWith('_')) {
|
|
486
|
+
console.log(`Skipping hand-authored file: ${file}`);
|
|
487
|
+
continue;
|
|
488
|
+
}
|
|
489
|
+
if (!currentNames.has(name)) {
|
|
490
|
+
const filePath = path.join(outRoot, file);
|
|
491
|
+
fs.unlinkSync(filePath);
|
|
492
|
+
console.log(`Deleted removed ${folder} partial: ${file}`);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
|
|
475
497
|
// Use custom or default template
|
|
476
498
|
const bloblangTemplatePath = templateBloblang || path.resolve(__dirname, './templates/bloblang-function.hbs');
|
|
477
499
|
const bloblangTemplate = handlebars.compile(fs.readFileSync(bloblangTemplatePath, 'utf8'));
|
|
@@ -484,6 +506,71 @@ async function generateRpcnConnectorDocs(options) {
|
|
|
484
506
|
partialFiles.push(path.relative(process.cwd(), outPath));
|
|
485
507
|
}
|
|
486
508
|
}
|
|
509
|
+
|
|
510
|
+
// Generate overview pages for Bloblang methods and functions
|
|
511
|
+
const guidesRoot = path.join(process.cwd(), 'modules/guides/pages/bloblang');
|
|
512
|
+
fs.mkdirSync(guidesRoot, { recursive: true });
|
|
513
|
+
|
|
514
|
+
// Generate methods.adoc with categorized methods
|
|
515
|
+
if (dataObj['bloblang-methods']) {
|
|
516
|
+
const methodsData = dataObj['bloblang-methods'];
|
|
517
|
+
const categoriesMap = new Map();
|
|
518
|
+
|
|
519
|
+
// Extract categories and their methods
|
|
520
|
+
for (const method of methodsData) {
|
|
521
|
+
if (!method.name || !method.categories || !Array.isArray(method.categories)) continue;
|
|
522
|
+
|
|
523
|
+
for (const cat of method.categories) {
|
|
524
|
+
const categoryName = cat.Category;
|
|
525
|
+
if (!categoryName) continue;
|
|
526
|
+
|
|
527
|
+
if (!categoriesMap.has(categoryName)) {
|
|
528
|
+
categoriesMap.set(categoryName, []);
|
|
529
|
+
}
|
|
530
|
+
categoriesMap.get(categoryName).push(method.name);
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
// Sort categories and prepare data for template
|
|
535
|
+
const categories = Array.from(categoriesMap.entries())
|
|
536
|
+
.map(([name, methods]) => ({
|
|
537
|
+
name,
|
|
538
|
+
methods: methods.sort()
|
|
539
|
+
}))
|
|
540
|
+
.sort((a, b) => {
|
|
541
|
+
// Special ordering: General first, Deprecated last
|
|
542
|
+
if (a.name === 'General') return -1;
|
|
543
|
+
if (b.name === 'General') return 1;
|
|
544
|
+
if (a.name === 'Deprecated') return 1;
|
|
545
|
+
if (b.name === 'Deprecated') return -1;
|
|
546
|
+
return a.name.localeCompare(b.name);
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
const methodsTemplatePath = path.resolve(__dirname, './templates/bloblang-methods-overview.hbs');
|
|
550
|
+
const methodsTemplate = handlebars.compile(fs.readFileSync(methodsTemplatePath, 'utf8'));
|
|
551
|
+
const methodsAdoc = methodsTemplate({ categories });
|
|
552
|
+
const methodsOutPath = path.join(guidesRoot, 'methods.adoc');
|
|
553
|
+
fs.writeFileSync(methodsOutPath, methodsAdoc, 'utf8');
|
|
554
|
+
console.log('Generated methods.adoc overview page');
|
|
555
|
+
partialFiles.push(path.relative(process.cwd(), methodsOutPath));
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Generate functions.adoc with all functions
|
|
559
|
+
if (dataObj['bloblang-functions']) {
|
|
560
|
+
const functionsData = dataObj['bloblang-functions'];
|
|
561
|
+
const functionNames = functionsData
|
|
562
|
+
.filter(fn => fn.name)
|
|
563
|
+
.map(fn => fn.name)
|
|
564
|
+
.sort();
|
|
565
|
+
|
|
566
|
+
const functionsTemplatePath = path.resolve(__dirname, './templates/bloblang-functions-overview.hbs');
|
|
567
|
+
const functionsTemplate = handlebars.compile(fs.readFileSync(functionsTemplatePath, 'utf8'));
|
|
568
|
+
const functionsAdoc = functionsTemplate({ functions: functionNames });
|
|
569
|
+
const functionsOutPath = path.join(guidesRoot, 'functions.adoc');
|
|
570
|
+
fs.writeFileSync(functionsOutPath, functionsAdoc, 'utf8');
|
|
571
|
+
console.log('Generated functions.adoc overview page');
|
|
572
|
+
partialFiles.push(path.relative(process.cwd(), functionsOutPath));
|
|
573
|
+
}
|
|
487
574
|
}
|
|
488
575
|
|
|
489
576
|
// Common/Advanced config snippet YAMLs in modules/components/examples
|
|
@@ -1,10 +1,30 @@
|
|
|
1
1
|
// Bloblang example formatting helper for Handlebars
|
|
2
2
|
function bloblangExample(example) {
|
|
3
3
|
if (typeof example === 'object' && example !== null && example.mapping) {
|
|
4
|
+
let leadIn = '';
|
|
4
5
|
let codeBlock = '';
|
|
6
|
+
|
|
7
|
+
// Extract summary as lead-in prose (not a comment in code)
|
|
5
8
|
if (example.summary && example.summary.trim()) {
|
|
6
|
-
|
|
9
|
+
let summary = example.summary.trim();
|
|
10
|
+
|
|
11
|
+
// Convert Markdown headings to AsciiDoc
|
|
12
|
+
// ##### Heading -> ==== Heading (H5 -> H4 in AsciiDoc)
|
|
13
|
+
summary = summary.replace(/^#####\s+(.+)$/gm, '==== $1');
|
|
14
|
+
// #### Heading -> === Heading (H4 -> H3 in AsciiDoc)
|
|
15
|
+
summary = summary.replace(/^####\s+(.+)$/gm, '=== $1');
|
|
16
|
+
// ### Heading -> == Heading (H3 -> H2 in AsciiDoc)
|
|
17
|
+
summary = summary.replace(/^###\s+(.+)$/gm, '== $1');
|
|
18
|
+
|
|
19
|
+
// Ensure lead-in ends with a colon (replace period/exclamation/question mark if present)
|
|
20
|
+
if (summary.endsWith('.') || summary.endsWith('!') || summary.endsWith('?')) {
|
|
21
|
+
summary = summary.slice(0, -1) + ':';
|
|
22
|
+
} else if (!summary.endsWith(':')) {
|
|
23
|
+
summary += ':';
|
|
24
|
+
}
|
|
25
|
+
leadIn = summary + '\n\n';
|
|
7
26
|
}
|
|
27
|
+
|
|
8
28
|
if (typeof example.mapping === 'string') {
|
|
9
29
|
codeBlock += example.mapping.trim() + '\n';
|
|
10
30
|
}
|
|
@@ -15,7 +35,7 @@ function bloblangExample(example) {
|
|
|
15
35
|
}
|
|
16
36
|
}
|
|
17
37
|
}
|
|
18
|
-
return
|
|
38
|
+
return `${leadIn}[,bloblang]\n----\n${codeBlock.trim()}\n----\n`;
|
|
19
39
|
} else {
|
|
20
40
|
let exStr = '';
|
|
21
41
|
if (typeof example === 'string') {
|
|
@@ -35,7 +55,7 @@ function bloblangExample(example) {
|
|
|
35
55
|
} else {
|
|
36
56
|
exStr = String(example);
|
|
37
57
|
}
|
|
38
|
-
return `[source,
|
|
58
|
+
return `[source,bloblang]\n----\n${exStr}\n----\n`;
|
|
39
59
|
}
|
|
40
60
|
}
|
|
41
61
|
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ensures a text string ends with a period (or other terminal punctuation).
|
|
3
|
+
* Used for normalizing Bloblang descriptions.
|
|
4
|
+
*
|
|
5
|
+
* @param {string} text - The text to process
|
|
6
|
+
* @returns {string} Text ending with appropriate punctuation
|
|
7
|
+
*/
|
|
8
|
+
function ensurePeriod(text) {
|
|
9
|
+
if (!text || typeof text !== 'string') {
|
|
10
|
+
return text;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const trimmed = text.trim();
|
|
14
|
+
if (!trimmed) {
|
|
15
|
+
return text;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Check if already ends with terminal punctuation
|
|
19
|
+
if (trimmed.endsWith('.') || trimmed.endsWith('!') || trimmed.endsWith('?')) {
|
|
20
|
+
return text;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Add period
|
|
24
|
+
return text.trim() + '.';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
module.exports = ensurePeriod;
|
|
@@ -17,4 +17,7 @@ module.exports = {
|
|
|
17
17
|
commonConfig: require('./commonConfig.js'),
|
|
18
18
|
advancedConfig: require('./advancedConfig.js'),
|
|
19
19
|
bloblangExample: require('./bloblangExample.js'),
|
|
20
|
+
hasOptionalParams: require('./hasOptionalParams.js'),
|
|
21
|
+
ensurePeriod: require('./ensurePeriod.js'),
|
|
22
|
+
toSentenceCase: require('./toSentenceCase.js'),
|
|
20
23
|
};
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
'use strict'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Convert a string to sentence case, preserving acronyms and proper nouns
|
|
5
|
+
* @param {string} text - The text to convert
|
|
6
|
+
* @returns {string} The text in sentence case
|
|
7
|
+
*/
|
|
8
|
+
function toSentenceCase(text) {
|
|
9
|
+
if (!text) return ''
|
|
10
|
+
|
|
11
|
+
// Map of exact word matches to preserve (case-sensitive)
|
|
12
|
+
const exactPreserve = new Map([
|
|
13
|
+
['geoip', 'GeoIP'],
|
|
14
|
+
['GEOIP', 'GeoIP'],
|
|
15
|
+
['GeoIP', 'GeoIP']
|
|
16
|
+
])
|
|
17
|
+
|
|
18
|
+
// List of acronyms to preserve (will be uppercased)
|
|
19
|
+
const preserveWords = new Set([
|
|
20
|
+
'SQL',
|
|
21
|
+
'JSON',
|
|
22
|
+
'JWT',
|
|
23
|
+
'XML',
|
|
24
|
+
'HTML',
|
|
25
|
+
'URL',
|
|
26
|
+
'URI',
|
|
27
|
+
'HTTP',
|
|
28
|
+
'HTTPS',
|
|
29
|
+
'TLS',
|
|
30
|
+
'SSL',
|
|
31
|
+
'AWS',
|
|
32
|
+
'GCP',
|
|
33
|
+
'API',
|
|
34
|
+
'ID',
|
|
35
|
+
'UUID',
|
|
36
|
+
'CSV'
|
|
37
|
+
])
|
|
38
|
+
|
|
39
|
+
// Split into words
|
|
40
|
+
const words = text.split(/\s+/)
|
|
41
|
+
|
|
42
|
+
return words.map((word, index) => {
|
|
43
|
+
// Check if word is in exact preserve map first
|
|
44
|
+
if (exactPreserve.has(word) || exactPreserve.has(word.toLowerCase()) || exactPreserve.has(word.toUpperCase())) {
|
|
45
|
+
const key = exactPreserve.has(word) ? word : (exactPreserve.has(word.toLowerCase()) ? word.toLowerCase() : word.toUpperCase())
|
|
46
|
+
return exactPreserve.get(key)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Check if word is in preserve list (case-insensitive check)
|
|
50
|
+
if (preserveWords.has(word.toUpperCase())) {
|
|
51
|
+
return word.toUpperCase()
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Check if word contains special characters like &
|
|
55
|
+
if (word === '&') {
|
|
56
|
+
return word
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// First word: capitalize first letter, lowercase rest
|
|
60
|
+
if (index === 0) {
|
|
61
|
+
return word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Subsequent words: lowercase
|
|
65
|
+
return word.toLowerCase()
|
|
66
|
+
}).join(' ')
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
module.exports = toSentenceCase
|