@redpanda-data/docs-extensions-and-macros 4.6.0 → 4.6.2
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.js +31 -19
- package/cli-utils/generate-cluster-docs.sh +41 -29
- package/cli-utils/start-cluster.sh +70 -30
- package/extensions/generate-rp-connect-info.js +14 -9
- package/package.json +1 -1
- package/tools/redpanda-connect/generate-rpcn-connector-docs.js +29 -1
- package/tools/redpanda-connect/helpers/renderConnectFields.js +82 -85
- package/tools/redpanda-connect/helpers/renderLeafField.js +40 -17
- package/tools/redpanda-connect/helpers/renderYamlList.js +20 -6
- package/tools/redpanda-connect/templates/intro.hbs +0 -2
package/bin/doc-tools.js
CHANGED
|
@@ -269,7 +269,11 @@ function verifyMetricsDependencies() {
|
|
|
269
269
|
// --------------------------------------------------------------------
|
|
270
270
|
const programCli = new Command();
|
|
271
271
|
|
|
272
|
-
|
|
272
|
+
const pkg = require('../package.json');
|
|
273
|
+
programCli
|
|
274
|
+
.name('doc-tools')
|
|
275
|
+
.description('Redpanda Document Automation CLI')
|
|
276
|
+
.version(pkg.version);
|
|
273
277
|
|
|
274
278
|
// Top-level commands.
|
|
275
279
|
programCli
|
|
@@ -542,22 +546,27 @@ automation
|
|
|
542
546
|
}
|
|
543
547
|
|
|
544
548
|
if (options.draftMissing) {
|
|
545
|
-
console.log('⏳ Drafting missing connectors
|
|
549
|
+
console.log('⏳ Drafting missing connectors…');
|
|
546
550
|
try {
|
|
547
551
|
const connectorList = await parseCSVConnectors(options.csv, console);
|
|
548
|
-
const validConnectors = connectorList.filter(
|
|
552
|
+
const validConnectors = connectorList.filter(r => r.name && r.type);
|
|
553
|
+
|
|
554
|
+
const roots = {
|
|
555
|
+
pages: path.resolve(process.cwd(), 'modules/components/pages'),
|
|
556
|
+
partials:path.resolve(process.cwd(), 'modules/components/partials/components'),
|
|
557
|
+
};
|
|
549
558
|
|
|
550
|
-
|
|
559
|
+
// find any connector that has NO .adoc under pages/TYPEs or partials/TYPEs
|
|
551
560
|
const allMissing = validConnectors.filter(({ name, type }) => {
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
return !fs.existsSync(expected);
|
|
561
|
+
const relPath = path.join(`${type}s`, `${name}.adoc`);
|
|
562
|
+
const existsInAny = Object.values(roots).some(root =>
|
|
563
|
+
fs.existsSync(path.join(root, relPath))
|
|
564
|
+
);
|
|
565
|
+
return !existsInAny;
|
|
558
566
|
});
|
|
559
567
|
|
|
560
|
-
|
|
568
|
+
// still skip sql_driver
|
|
569
|
+
const missingConnectors = allMissing.filter(c => !c.name.includes('sql_driver'));
|
|
561
570
|
|
|
562
571
|
if (missingConnectors.length === 0) {
|
|
563
572
|
console.log('✅ All connectors (excluding sql_drivers) already have docs—nothing to draft.');
|
|
@@ -568,17 +577,20 @@ automation
|
|
|
568
577
|
});
|
|
569
578
|
console.log('');
|
|
570
579
|
|
|
580
|
+
// build your filtered JSON as before…
|
|
571
581
|
const rawData = fs.readFileSync(dataFile, 'utf8');
|
|
572
582
|
const dataObj = JSON.parse(rawData);
|
|
573
|
-
|
|
574
583
|
const filteredDataObj = {};
|
|
584
|
+
|
|
575
585
|
for (const [key, arr] of Object.entries(dataObj)) {
|
|
576
586
|
if (!Array.isArray(arr)) {
|
|
577
587
|
filteredDataObj[key] = arr;
|
|
578
588
|
continue;
|
|
579
589
|
}
|
|
580
590
|
filteredDataObj[key] = arr.filter(component =>
|
|
581
|
-
missingConnectors.some(
|
|
591
|
+
missingConnectors.some(
|
|
592
|
+
m => m.name === component.name && `${m.type}s` === key
|
|
593
|
+
)
|
|
582
594
|
);
|
|
583
595
|
}
|
|
584
596
|
|
|
@@ -586,12 +598,12 @@ automation
|
|
|
586
598
|
fs.writeFileSync(tempDataPath, JSON.stringify(filteredDataObj, null, 2), 'utf8');
|
|
587
599
|
|
|
588
600
|
const draftResult = await generateRpcnConnectorDocs({
|
|
589
|
-
data:
|
|
590
|
-
overrides:
|
|
591
|
-
template:
|
|
592
|
-
templateFields:
|
|
593
|
-
templateExamples:
|
|
594
|
-
templateIntro:
|
|
601
|
+
data: tempDataPath,
|
|
602
|
+
overrides: options.overrides,
|
|
603
|
+
template: options.templateMain,
|
|
604
|
+
templateFields: options.templateFields,
|
|
605
|
+
templateExamples:options.templateExamples,
|
|
606
|
+
templateIntro: options.templateIntro,
|
|
595
607
|
writeFullDrafts: true
|
|
596
608
|
});
|
|
597
609
|
|
|
@@ -1,23 +1,34 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
|
+
IFS=$'\n\t'
|
|
3
4
|
|
|
4
|
-
|
|
5
|
+
###############################################################################
|
|
6
|
+
# Pre-flight: Ensure Docker is available and running
|
|
7
|
+
###############################################################################
|
|
5
8
|
if ! command -v docker &> /dev/null; then
|
|
6
9
|
echo "❌ Docker is not installed or not in PATH. Please install Docker to continue."
|
|
7
10
|
exit 1
|
|
8
11
|
fi
|
|
9
12
|
|
|
10
|
-
# Check if Docker daemon is running
|
|
11
13
|
if ! docker info &> /dev/null; then
|
|
12
14
|
echo "❌ Docker daemon is not running. Please start Docker to continue."
|
|
13
15
|
exit 1
|
|
14
16
|
fi
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
###############################################################################
|
|
19
|
+
# Load overrides from an optional .env file in the current directory
|
|
20
|
+
###############################################################################
|
|
21
|
+
if [[ -f .env ]]; then
|
|
22
|
+
# shellcheck disable=SC2046
|
|
23
|
+
export $(grep -Ev '^#' .env | xargs)
|
|
24
|
+
fi
|
|
18
25
|
|
|
19
|
-
|
|
26
|
+
###############################################################################
|
|
27
|
+
# Environment setup
|
|
28
|
+
###############################################################################
|
|
29
|
+
ORIGINAL_PWD="$(pwd)"
|
|
20
30
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
31
|
+
PROJECT_NAME="${PROJECT_NAME:-redpanda_quickstart}"
|
|
21
32
|
|
|
22
33
|
MODE="${1:-metrics}"
|
|
23
34
|
TAG="${2:-latest}"
|
|
@@ -25,16 +36,15 @@ DOCKER_REPO="${3:-redpanda}"
|
|
|
25
36
|
CONSOLE_TAG="${4:-latest}"
|
|
26
37
|
CONSOLE_REPO="${5:-console}"
|
|
27
38
|
|
|
28
|
-
#
|
|
39
|
+
# Adjust Docker repo for release candidates
|
|
29
40
|
shopt -s nocasematch
|
|
30
41
|
if [[ "$TAG" =~ rc[0-9]+ ]]; then
|
|
31
42
|
DOCKER_REPO="redpanda-unstable"
|
|
32
43
|
fi
|
|
33
44
|
shopt -u nocasematch
|
|
34
45
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
else
|
|
46
|
+
MAJOR_MINOR="latest"
|
|
47
|
+
if [[ "$TAG" != "latest" ]]; then
|
|
38
48
|
MAJOR_MINOR="$(echo "$TAG" | sed -E 's/^v?([0-9]+\.[0-9]+).*$/\1/')"
|
|
39
49
|
fi
|
|
40
50
|
|
|
@@ -43,41 +53,43 @@ export REDPANDA_DOCKER_REPO="$DOCKER_REPO"
|
|
|
43
53
|
export REDPANDA_CONSOLE_VERSION="$CONSOLE_TAG"
|
|
44
54
|
export REDPANDA_CONSOLE_DOCKER_REPO="$CONSOLE_REPO"
|
|
45
55
|
|
|
46
|
-
|
|
47
|
-
|
|
56
|
+
###############################################################################
|
|
57
|
+
# Start Redpanda cluster
|
|
58
|
+
###############################################################################
|
|
59
|
+
"$SCRIPT_DIR/start-cluster.sh" "$TAG"
|
|
48
60
|
|
|
49
|
-
# Wait for
|
|
61
|
+
# Wait for the cluster to settle
|
|
50
62
|
if [[ "$MODE" == "metrics" ]]; then
|
|
51
|
-
echo "Waiting 300 seconds for metrics to be available…"
|
|
63
|
+
echo "⏳ Waiting 300 seconds for metrics to be available…"
|
|
52
64
|
sleep 300
|
|
53
65
|
else
|
|
54
|
-
echo "Waiting 30 seconds for cluster to be ready…"
|
|
66
|
+
echo "⏳ Waiting 30 seconds for cluster to be ready…"
|
|
55
67
|
sleep 30
|
|
56
68
|
fi
|
|
57
69
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"$SCRIPT_DIR
|
|
63
|
-
"$SCRIPT_DIR"
|
|
64
|
-
"$SCRIPT_DIR"/../tools/metrics/requirements.txt
|
|
70
|
+
###############################################################################
|
|
71
|
+
# Python virtual environment setup
|
|
72
|
+
###############################################################################
|
|
73
|
+
"$SCRIPT_DIR/python-venv.sh" \
|
|
74
|
+
"$SCRIPT_DIR/venv" \
|
|
75
|
+
"$SCRIPT_DIR/../tools/metrics/requirements.txt"
|
|
65
76
|
|
|
77
|
+
###############################################################################
|
|
78
|
+
# Run documentation generator
|
|
79
|
+
###############################################################################
|
|
66
80
|
if [[ "$MODE" == "metrics" ]]; then
|
|
67
|
-
"$SCRIPT_DIR
|
|
68
|
-
"$SCRIPT_DIR
|
|
69
|
-
"$TAG"
|
|
81
|
+
"$SCRIPT_DIR/venv/bin/python" \
|
|
82
|
+
"$SCRIPT_DIR/../tools/metrics/metrics.py" "$TAG"
|
|
70
83
|
else
|
|
71
|
-
"$SCRIPT_DIR
|
|
72
|
-
"$SCRIPT_DIR
|
|
73
|
-
"$TAG"
|
|
84
|
+
"$SCRIPT_DIR/venv/bin/python" \
|
|
85
|
+
"$SCRIPT_DIR/../tools/gen-rpk-ascii.py" "$TAG"
|
|
74
86
|
fi
|
|
75
87
|
|
|
76
88
|
echo "✅ $MODE docs generated successfully!"
|
|
77
89
|
|
|
78
90
|
# Tear down the cluster
|
|
79
91
|
cd "$SCRIPT_DIR"/../docker-compose
|
|
80
|
-
docker compose down --volumes
|
|
92
|
+
docker compose -p "$PROJECT_NAME" down --volumes
|
|
81
93
|
|
|
82
94
|
# Return to the original directory
|
|
83
|
-
cd "$ORIGINAL_PWD"
|
|
95
|
+
cd "$ORIGINAL_PWD" || exit 1
|
|
@@ -1,63 +1,103 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
set -euo pipefail
|
|
3
|
+
IFS=$'\n\t'
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
5
|
+
###############################################################################
|
|
6
|
+
# Set START_CLUSTER_LOG=/path/to/logfile to capture output
|
|
7
|
+
###############################################################################
|
|
8
|
+
if [[ -n "${START_CLUSTER_LOG:-}" ]]; then
|
|
9
|
+
exec > >(tee -a "$START_CLUSTER_LOG") 2>&1
|
|
10
|
+
fi
|
|
6
11
|
|
|
7
|
-
|
|
8
|
-
|
|
12
|
+
###############################################################################
|
|
13
|
+
# Prevent concurrent runs with a simple lockfile
|
|
14
|
+
###############################################################################
|
|
15
|
+
LOCKFILE="/tmp/start-cluster.lock"
|
|
16
|
+
if [[ -e "$LOCKFILE" ]]; then
|
|
17
|
+
echo "❗ Another start-cluster.sh is already running. Remove $LOCKFILE if stale." >&2
|
|
18
|
+
exit 1
|
|
19
|
+
fi
|
|
20
|
+
trap 'rm -f "$LOCKFILE"' EXIT
|
|
21
|
+
touch "$LOCKFILE"
|
|
9
22
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
23
|
+
###############################################################################
|
|
24
|
+
# Input parameters and defaults
|
|
25
|
+
###############################################################################
|
|
26
|
+
TAG="${1:-${CLUSTER_TAG:-latest}}"
|
|
27
|
+
PROJECT_NAME="${PROJECT_NAME:-redpanda_quickstart}"
|
|
13
28
|
|
|
14
|
-
|
|
29
|
+
###############################################################################
|
|
30
|
+
# Directory discovery
|
|
31
|
+
###############################################################################
|
|
32
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
33
|
+
PACKAGE_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
34
|
+
QUICKSTART_DIR="$PACKAGE_ROOT/docker-compose"
|
|
15
35
|
CALLER_PWD="$(pwd)"
|
|
16
36
|
|
|
17
|
-
|
|
37
|
+
###############################################################################
|
|
38
|
+
# Determine major.minor for quickstart override
|
|
39
|
+
###############################################################################
|
|
18
40
|
MAJOR_MINOR="latest"
|
|
19
41
|
if [[ "$TAG" != "latest" ]]; then
|
|
20
42
|
MAJOR_MINOR="$(echo "$TAG" | sed -E 's/^v?([0-9]+\.[0-9]+).*$/\1/')"
|
|
21
43
|
fi
|
|
22
44
|
|
|
23
|
-
# Conditionally override quickstart dir for >= 25.1
|
|
24
45
|
if [[ "$MAJOR_MINOR" =~ ^([0-9]+)\.([0-9]+)$ ]]; then
|
|
25
46
|
major="${BASH_REMATCH[1]}"
|
|
26
47
|
minor="${BASH_REMATCH[2]}"
|
|
27
|
-
|
|
28
48
|
if (( major > 25 )) || (( major == 25 && minor >= 1 )); then
|
|
29
49
|
QUICKSTART_DIR="$PACKAGE_ROOT/docker-compose/25.1"
|
|
30
50
|
fi
|
|
31
51
|
fi
|
|
32
52
|
|
|
33
|
-
|
|
53
|
+
###############################################################################
|
|
54
|
+
# Fetch quickstart bundle if missing
|
|
55
|
+
###############################################################################
|
|
34
56
|
if [[ ! -d "$QUICKSTART_DIR" ]]; then
|
|
35
57
|
echo "📥 Fetching Redpanda quickstart for ${MAJOR_MINOR}…"
|
|
58
|
+
url="https://docs.redpanda.com"
|
|
36
59
|
if [[ "$TAG" == "latest" ]]; then
|
|
37
|
-
|
|
38
|
-
| tar -C "$PACKAGE_ROOT" -xzf -
|
|
60
|
+
url="$url/redpanda-quickstart.tar.gz"
|
|
39
61
|
else
|
|
40
|
-
|
|
41
|
-
| tar -C "$PACKAGE_ROOT" -xzf -
|
|
42
|
-
fi
|
|
43
|
-
|
|
44
|
-
if [[ ! -d "$QUICKSTART_DIR" ]]; then
|
|
45
|
-
echo "❌ Expected '$QUICKSTART_DIR' but none was found after extraction."
|
|
46
|
-
exit 1
|
|
62
|
+
url="$url/${MAJOR_MINOR}-redpanda-quickstart.tar.gz"
|
|
47
63
|
fi
|
|
64
|
+
curl -sSLf --retry 3 "$url" | tar -C "$PACKAGE_ROOT" -xzf -
|
|
65
|
+
[[ -d "$QUICKSTART_DIR" ]] || { echo "❌ Expected '$QUICKSTART_DIR' but none was found."; exit 1; }
|
|
48
66
|
fi
|
|
49
67
|
|
|
50
|
-
|
|
51
|
-
|
|
68
|
+
###############################################################################
|
|
69
|
+
# Move into compose directory and clean up existing cluster
|
|
70
|
+
###############################################################################
|
|
71
|
+
cd "$QUICKSTART_DIR" || { echo "❌ Cannot cd to '$QUICKSTART_DIR'"; exit 1; }
|
|
52
72
|
|
|
53
|
-
if docker compose ps | grep -q
|
|
54
|
-
echo "🛑
|
|
55
|
-
docker compose down --volumes
|
|
73
|
+
if docker compose -p "$PROJECT_NAME" ps -q | grep -q .; then
|
|
74
|
+
echo "🛑 Cleaning up existing cluster…"
|
|
75
|
+
docker compose -p "$PROJECT_NAME" down --volumes
|
|
76
|
+
else
|
|
77
|
+
echo "No running containers to remove for project \"$PROJECT_NAME\"."
|
|
56
78
|
fi
|
|
57
79
|
|
|
58
|
-
|
|
59
|
-
|
|
80
|
+
###############################################################################
|
|
81
|
+
# Remove globally conflicting containers (dynamic list + legacy minio)
|
|
82
|
+
###############################################################################
|
|
83
|
+
services=$(docker compose -p "$PROJECT_NAME" config --services 2>/dev/null || true)
|
|
84
|
+
services+=" minio" # ensure legacy /minio container is handled
|
|
85
|
+
|
|
86
|
+
for svc in $services; do
|
|
87
|
+
if docker ps -a --format '{{.Names}}' | grep -wq "$svc"; then
|
|
88
|
+
echo "🧹 Removing existing container: $svc"
|
|
89
|
+
docker rm -f "$svc"
|
|
90
|
+
fi
|
|
91
|
+
done
|
|
92
|
+
|
|
93
|
+
###############################################################################
|
|
94
|
+
# Start cluster
|
|
95
|
+
###############################################################################
|
|
96
|
+
echo "▶️ Starting Redpanda cluster (version: ${TAG})…"
|
|
97
|
+
docker compose -p "$PROJECT_NAME" up -d
|
|
60
98
|
|
|
61
|
-
|
|
62
|
-
|
|
99
|
+
###############################################################################
|
|
100
|
+
# Return to caller and report success
|
|
101
|
+
###############################################################################
|
|
102
|
+
cd "$CALLER_PWD" || exit 1
|
|
63
103
|
echo "✅ Cluster is up (version: ${TAG})"
|
|
@@ -82,14 +82,14 @@ module.exports.register = function ({ config }) {
|
|
|
82
82
|
}
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
|
-
*
|
|
86
|
-
* If "enterprise" is found in the `support` column, it is replaced with "certified" in the output.
|
|
85
|
+
* Transforms and enriches parsed CSV connector data with normalized fields and documentation URLs.
|
|
87
86
|
*
|
|
88
|
-
*
|
|
89
|
-
* @param {array} pages - The list of pages to map the URLs (used for enrichment with URLs).
|
|
90
|
-
* @param {object} logger - The logger used for error handling.
|
|
87
|
+
* 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.
|
|
91
88
|
*
|
|
92
|
-
* @
|
|
89
|
+
* @param {object} parsedData - Parsed CSV data containing connector rows.
|
|
90
|
+
* @param {array} pages - Array of page objects used to resolve documentation URLs.
|
|
91
|
+
* @param {object} logger - Logger instance for warning about missing documentation.
|
|
92
|
+
* @returns {array} Array of enriched connector objects with normalized fields and URLs.
|
|
93
93
|
*/
|
|
94
94
|
function translateCsvData(parsedData, pages, logger) {
|
|
95
95
|
return parsedData.data.map(row => {
|
|
@@ -139,12 +139,17 @@ module.exports.register = function ({ config }) {
|
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
-
// Log a warning if neither URL was found
|
|
143
|
-
|
|
144
|
-
|
|
142
|
+
// Log a warning if neither URL was found and the component is not deprecated
|
|
143
|
+
if (
|
|
144
|
+
deprecated !== 'y' &&
|
|
145
|
+
!connector.includes('sql_driver') &&
|
|
146
|
+
!redpandaConnectUrl &&
|
|
147
|
+
(!redpandaCloudUrl && is_cloud_supported === 'y')
|
|
148
|
+
) {
|
|
145
149
|
logger.warn(`Docs missing for: ${connector} of type: ${type}`);
|
|
146
150
|
}
|
|
147
151
|
|
|
152
|
+
|
|
148
153
|
// Return the translated and enriched row
|
|
149
154
|
return {
|
|
150
155
|
connector,
|
package/package.json
CHANGED
|
@@ -54,6 +54,10 @@ function mergeOverrides(target, overrides) {
|
|
|
54
54
|
item[field] = overrideItem[field];
|
|
55
55
|
}
|
|
56
56
|
});
|
|
57
|
+
// Copy through selfManagedOnly flag
|
|
58
|
+
if (Object.hasOwn(overrideItem, 'selfManagedOnly')) {
|
|
59
|
+
item.selfManagedOnly = overrideItem.selfManagedOnly;
|
|
60
|
+
}
|
|
57
61
|
// Recurse for nested children
|
|
58
62
|
item = mergeOverrides(item, overrideItem);
|
|
59
63
|
}
|
|
@@ -75,6 +79,26 @@ function mergeOverrides(target, overrides) {
|
|
|
75
79
|
return target;
|
|
76
80
|
}
|
|
77
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Generates documentation files for RPCN connectors using Handlebars templates.
|
|
84
|
+
*
|
|
85
|
+
* Depending on the {@link writeFullDrafts} flag, generates either partial documentation files for connector fields and examples, or full draft documentation for each connector component. Supports merging override data and skips draft generation for components marked as deprecated.
|
|
86
|
+
*
|
|
87
|
+
* @param {Object} options - Configuration options for documentation generation.
|
|
88
|
+
* @param {string} options.data - Path to the connector data file (JSON or YAML).
|
|
89
|
+
* @param {string} [options.overrides] - Optional path to a JSON file with override data.
|
|
90
|
+
* @param {string} options.template - Path to the main Handlebars template.
|
|
91
|
+
* @param {string} [options.templateIntro] - Path to the intro partial template (used in full draft mode).
|
|
92
|
+
* @param {string} [options.templateFields] - Path to the fields partial template.
|
|
93
|
+
* @param {string} [options.templateExamples] - Path to the examples partial template.
|
|
94
|
+
* @param {boolean} options.writeFullDrafts - If true, generates full draft documentation; otherwise, generates partials.
|
|
95
|
+
* @returns {Promise<Object>} An object summarizing the number and paths of generated partials and drafts.
|
|
96
|
+
*
|
|
97
|
+
* @throws {Error} If reading or parsing input files fails, or if template rendering fails for a component.
|
|
98
|
+
*
|
|
99
|
+
* @remark
|
|
100
|
+
* When generating full drafts, components with a `status` of `'deprecated'` are skipped.
|
|
101
|
+
*/
|
|
78
102
|
async function generateRpcnConnectorDocs(options) {
|
|
79
103
|
const {
|
|
80
104
|
data,
|
|
@@ -161,7 +185,7 @@ async function generateRpcnConnectorDocs(options) {
|
|
|
161
185
|
partialFiles.push(path.relative(process.cwd(), fPath));
|
|
162
186
|
}
|
|
163
187
|
|
|
164
|
-
if (examplesOut.trim()) {
|
|
188
|
+
if (examplesOut.trim() && type !== 'bloblang-functions' && type !== 'bloblang-methods') {
|
|
165
189
|
const ePath = path.join(examplesOutRoot, type, `${name}.adoc`);
|
|
166
190
|
fs.mkdirSync(path.dirname(ePath), { recursive: true });
|
|
167
191
|
fs.writeFileSync(ePath, examplesOut);
|
|
@@ -171,6 +195,10 @@ async function generateRpcnConnectorDocs(options) {
|
|
|
171
195
|
}
|
|
172
196
|
|
|
173
197
|
if (writeFullDrafts) {
|
|
198
|
+
if (String(item.status || '').toLowerCase() === 'deprecated') {
|
|
199
|
+
console.log(`Skipping draft for deprecated component: ${type}/${name}`);
|
|
200
|
+
continue;
|
|
201
|
+
}
|
|
174
202
|
let content;
|
|
175
203
|
try {
|
|
176
204
|
content = compiledTemplate(item);
|
|
@@ -1,10 +1,7 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
1
|
const yaml = require('yaml');
|
|
4
2
|
const renderYamlList = require('./renderYamlList');
|
|
5
3
|
const handlebars = require('handlebars');
|
|
6
4
|
|
|
7
|
-
|
|
8
5
|
/**
|
|
9
6
|
* Renders the children of a configuration object into AsciiDoc.
|
|
10
7
|
*
|
|
@@ -27,120 +24,120 @@ module.exports = function renderConnectFields(children, prefix = '') {
|
|
|
27
24
|
prefix = typeof prefix === 'string' ? prefix : '';
|
|
28
25
|
|
|
29
26
|
sorted.forEach(child => {
|
|
30
|
-
if (child.is_deprecated)
|
|
31
|
-
|
|
27
|
+
if (child.is_deprecated || !child.name) return;
|
|
28
|
+
|
|
29
|
+
// Normalize type: arrays and unknown-map as object
|
|
30
|
+
let displayType;
|
|
31
|
+
if (child.type === 'string' && child.kind === 'array') {
|
|
32
|
+
displayType = 'array';
|
|
33
|
+
} else if (child.type === 'unknown' && child.kind === 'map') {
|
|
34
|
+
displayType = 'object';
|
|
35
|
+
} else {
|
|
36
|
+
displayType = child.type;
|
|
32
37
|
}
|
|
38
|
+
|
|
39
|
+
let block = '';
|
|
33
40
|
const isArray = child.kind === 'array';
|
|
34
|
-
if (!child.name) return;
|
|
35
41
|
const currentPath = prefix
|
|
36
42
|
? `${prefix}.${child.name}${isArray ? '[]' : ''}`
|
|
37
43
|
: `${child.name}${isArray ? '[]' : ''}`;
|
|
38
44
|
|
|
39
|
-
|
|
45
|
+
block += `=== \`${currentPath}\`\n\n`;
|
|
40
46
|
|
|
41
47
|
if (child.description) {
|
|
42
|
-
|
|
48
|
+
block += `${child.description}\n\n`;
|
|
43
49
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
output += `include::redpanda-connect:components:partial$secret_warning.adoc[]\n\n`;
|
|
50
|
+
if (child.is_secret) {
|
|
51
|
+
block += `include::redpanda-connect:components:partial$secret_warning.adoc[]\n\n`;
|
|
47
52
|
}
|
|
48
|
-
|
|
49
53
|
if (child.version) {
|
|
50
|
-
|
|
54
|
+
block += `ifndef::env-cloud[]\nRequires version ${child.version} or later.\nendif::[]\n\n`;
|
|
51
55
|
}
|
|
52
56
|
|
|
53
|
-
|
|
57
|
+
block += `*Type*: \`${displayType}\`\n\n`;
|
|
54
58
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
// Default value
|
|
60
|
+
if (child.default !== undefined) {
|
|
61
|
+
// Empty array
|
|
62
|
+
if (Array.isArray(child.default) && child.default.length === 0) {
|
|
63
|
+
block += `*Default*: \`[]\`\n\n`;
|
|
64
|
+
}
|
|
65
|
+
// Empty object
|
|
66
|
+
else if (
|
|
67
|
+
child.default !== null &&
|
|
68
|
+
typeof child.default === 'object' &&
|
|
69
|
+
!Array.isArray(child.default) &&
|
|
70
|
+
Object.keys(child.default).length === 0
|
|
71
|
+
) {
|
|
72
|
+
block += `*Default*: \`{}\`\n\n`;
|
|
73
|
+
}
|
|
74
|
+
// Complex object/array
|
|
75
|
+
else if (typeof child.default === 'object') {
|
|
60
76
|
const defYaml = yaml.stringify(child.default).trim();
|
|
61
|
-
|
|
77
|
+
block += `*Default*:\n[source,yaml]\n----\n${defYaml}\n----\n\n`;
|
|
78
|
+
}
|
|
79
|
+
// Primitive
|
|
80
|
+
else {
|
|
81
|
+
const display = typeof child.default === 'string'
|
|
82
|
+
? (child.default.startsWith('"') && child.default.endsWith('"')
|
|
83
|
+
? child.default
|
|
84
|
+
: child.default === ''
|
|
85
|
+
? '""'
|
|
86
|
+
: child.default)
|
|
87
|
+
: String(child.default);
|
|
88
|
+
block += `*Default*: \`${display}\`\n\n`;
|
|
62
89
|
}
|
|
63
90
|
}
|
|
64
91
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
child.annotated_options.
|
|
69
|
-
|
|
70
|
-
output += '[cols="1m,2a"]\n';
|
|
71
|
-
output += '|===\n';
|
|
72
|
-
output += '|Option |Summary\n\n';
|
|
73
|
-
child.annotated_options.forEach(optionPair => {
|
|
74
|
-
if (Array.isArray(optionPair) && optionPair.length >= 2) {
|
|
75
|
-
output += `|${optionPair[0]}\n|${optionPair[1]}\n\n`;
|
|
76
|
-
}
|
|
92
|
+
// Annotated options table
|
|
93
|
+
if (child.annotated_options && child.annotated_options.length) {
|
|
94
|
+
block += `[cols="1m,2a"]\n|===\n|Option |Summary\n\n`;
|
|
95
|
+
child.annotated_options.forEach(([opt, summary]) => {
|
|
96
|
+
block += `|${opt}\n|${summary}\n\n`;
|
|
77
97
|
});
|
|
78
|
-
|
|
98
|
+
block += `|===\n\n`;
|
|
79
99
|
}
|
|
80
100
|
|
|
81
|
-
|
|
82
|
-
|
|
101
|
+
// Simple options list
|
|
102
|
+
if (child.options && child.options.length) {
|
|
103
|
+
block += `*Options*: ${child.options.map(opt => `\`${opt}\``).join(', ')}\n\n`;
|
|
83
104
|
}
|
|
84
105
|
|
|
106
|
+
// Examples
|
|
85
107
|
if (child.examples && child.examples.length) {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
if (child.type === 'string') {
|
|
90
|
-
if (child.kind === 'array') {
|
|
91
|
-
output += renderYamlList(child.name, child.examples);
|
|
92
|
-
} else {
|
|
93
|
-
child.examples.forEach(example => {
|
|
94
|
-
if (typeof example === 'string' && example.includes('\n')) {
|
|
95
|
-
output += `${child.name}: |-\n`;
|
|
96
|
-
const indentedLines = example.split('\n').map(line => ' ' + line).join('\n');
|
|
97
|
-
output += `${indentedLines}\n`;
|
|
98
|
-
} else {
|
|
99
|
-
output += `${child.name}: ${example}\n`;
|
|
100
|
-
}
|
|
101
|
-
});
|
|
102
|
-
output += '\n';
|
|
103
|
-
}
|
|
104
|
-
} else if (child.type === 'processor') {
|
|
105
|
-
if (child.kind === 'array') {
|
|
106
|
-
output += renderYamlList(child.name, child.examples);
|
|
107
|
-
} else {
|
|
108
|
-
child.examples.forEach(example => {
|
|
109
|
-
output += `${child.name}: ${example}\n`;
|
|
110
|
-
});
|
|
111
|
-
output += '\n';
|
|
112
|
-
}
|
|
113
|
-
} else if (child.type === 'object') {
|
|
114
|
-
if (child.kind === 'array') {
|
|
115
|
-
output += renderYamlList(child.name, child.examples);
|
|
116
|
-
} else {
|
|
117
|
-
child.examples.forEach(example => {
|
|
118
|
-
if (typeof example === 'object') {
|
|
119
|
-
const snippet = yaml.stringify(example).trim();
|
|
120
|
-
const lines = snippet.split('\n');
|
|
121
|
-
// Prefix two spaces to every line
|
|
122
|
-
const formattedLines = lines.map(line => ' ' + line).join('\n');
|
|
123
|
-
output += `${child.name}:\n${formattedLines}\n`;
|
|
124
|
-
} else {
|
|
125
|
-
output += `${child.name}: ${example}\n`;
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
output += '\n';
|
|
129
|
-
}
|
|
108
|
+
block += `[source,yaml]\n----\n# Examples:\n`;
|
|
109
|
+
if (child.kind === 'array') {
|
|
110
|
+
block += renderYamlList(child.name, child.examples);
|
|
130
111
|
} else {
|
|
131
112
|
child.examples.forEach(example => {
|
|
132
|
-
|
|
113
|
+
if (typeof example === 'object') {
|
|
114
|
+
const snippet = yaml.stringify(example).trim();
|
|
115
|
+
block += `${child.name}:\n`;
|
|
116
|
+
block += snippet.split('\n').map(line => ' ' + line).join('\n') + '\n';
|
|
117
|
+
} else if (typeof example === 'string' && example.includes('\n')) {
|
|
118
|
+
block += `${child.name}: |-\n`;
|
|
119
|
+
block += example.split('\n').map(line => ' ' + line).join('\n') + '\n';
|
|
120
|
+
} else {
|
|
121
|
+
// Primitive values
|
|
122
|
+
block += `${child.name}: ${example}\n`;
|
|
123
|
+
}
|
|
133
124
|
});
|
|
134
|
-
output += '\n';
|
|
135
125
|
}
|
|
126
|
+
block += `----\n\n`;
|
|
127
|
+
}
|
|
136
128
|
|
|
137
|
-
|
|
129
|
+
// Nested children
|
|
130
|
+
if (child.children && child.children.length) {
|
|
131
|
+
block += renderConnectFields(child.children, currentPath);
|
|
138
132
|
}
|
|
139
133
|
|
|
140
|
-
|
|
141
|
-
|
|
134
|
+
// Cloud guard
|
|
135
|
+
if (child.selfManagedOnly) {
|
|
136
|
+
output += `ifndef::env-cloud[]\n${block}endif::[]\n\n`;
|
|
137
|
+
} else {
|
|
138
|
+
output += block;
|
|
142
139
|
}
|
|
143
140
|
});
|
|
144
141
|
|
|
145
142
|
return new handlebars.SafeString(output);
|
|
146
|
-
}
|
|
143
|
+
};
|
|
@@ -31,34 +31,57 @@ module.exports = function renderLeafField(field, indentLevel) {
|
|
|
31
31
|
|
|
32
32
|
// If a default is provided, use it:
|
|
33
33
|
if (field.default !== undefined) {
|
|
34
|
-
//
|
|
34
|
+
// Empty array inline
|
|
35
|
+
if (Array.isArray(field.default) && field.default.length === 0) {
|
|
36
|
+
return `${indent}${name}: []`;
|
|
37
|
+
}
|
|
38
|
+
// Empty object inline
|
|
39
|
+
if (
|
|
40
|
+
field.default !== null &&
|
|
41
|
+
typeof field.default === 'object' &&
|
|
42
|
+
!Array.isArray(field.default) &&
|
|
43
|
+
Object.keys(field.default).length === 0
|
|
44
|
+
) {
|
|
45
|
+
return `${indent}${name}: {}`;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Complex object/array: dump as YAML block
|
|
35
49
|
if (typeof field.default === 'object') {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
// Indent each line of rawYaml by (indentLevel + 2) spaces:
|
|
40
|
-
const indentedYaml = rawYaml
|
|
50
|
+
try {
|
|
51
|
+
const rawYaml = yaml.stringify(field.default).trim();
|
|
52
|
+
const indentedYaml = rawYaml
|
|
41
53
|
.split('\n')
|
|
42
54
|
.map(line => ' '.repeat(indentLevel + 2) + line)
|
|
43
55
|
.join('\n');
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
56
|
+
return `${indent}${name}:\n${indentedYaml}`;
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.warn(`Failed to serialize default for ${field.name}:`, error);
|
|
59
|
+
return `${indent}${name}: {} # Error serializing default`;
|
|
60
|
+
}
|
|
49
61
|
}
|
|
50
62
|
|
|
51
|
-
//
|
|
52
|
-
|
|
53
|
-
|
|
63
|
+
// Primitive default: string, number, boolean
|
|
64
|
+
let value;
|
|
65
|
+
if (typeof field.default === 'string') {
|
|
66
|
+
// Preserve existing quotes
|
|
67
|
+
if (field.default.startsWith('"') && field.default.endsWith('"')) {
|
|
68
|
+
value = field.default;
|
|
69
|
+
} else if (field.default === '') {
|
|
70
|
+
value = '""';
|
|
71
|
+
} else {
|
|
72
|
+
value = field.default;
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
value = String(field.default);
|
|
54
76
|
}
|
|
55
|
-
|
|
77
|
+
|
|
78
|
+
return `${indent}${name}: ${value}`;
|
|
56
79
|
}
|
|
57
80
|
|
|
58
|
-
// No default → choose representation
|
|
81
|
+
// No default → choose representation
|
|
59
82
|
if (field.kind === 'array') {
|
|
60
83
|
return `${indent}${name}: [] ${comment}`;
|
|
61
84
|
} else {
|
|
62
85
|
return `${indent}${name}: "" ${comment}`;
|
|
63
86
|
}
|
|
64
|
-
}
|
|
87
|
+
};
|
|
@@ -12,13 +12,27 @@ module.exports = function renderYamlList(name, exampleGroups) {
|
|
|
12
12
|
exampleGroups.forEach(group => {
|
|
13
13
|
const items = Array.isArray(group) ? group : [group];
|
|
14
14
|
items.forEach(item => {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
// Scalars
|
|
16
|
+
if (typeof item === 'string' || typeof item === 'number' || typeof item === 'boolean') {
|
|
17
|
+
let value = String(item);
|
|
18
|
+
// Quote when needed: already-quoted scalars stay as-is; otherwise quote `*`
|
|
19
|
+
// and any value containing YAML-special characters.
|
|
20
|
+
if (!(value.startsWith('"') && value.endsWith('"'))) {
|
|
21
|
+
if (value === '*' || /[:\[\]\{\},&>|%@`]/.test(value)) {
|
|
22
|
+
value = `"${value}"`;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
out += ` - ${value}\n`;
|
|
26
|
+
} else {
|
|
27
|
+
// Objects/arrays: stringify with indentation
|
|
28
|
+
const snippet = yaml.stringify(item).trim();
|
|
29
|
+
const lines = snippet.split('\n');
|
|
30
|
+
out += lines
|
|
31
|
+
.map((line, idx) => (idx === 0 ? ` - ${line}` : ` ${line}`))
|
|
32
|
+
.join('\n') + '\n';
|
|
33
|
+
}
|
|
20
34
|
});
|
|
21
35
|
out += '\n';
|
|
22
36
|
});
|
|
23
37
|
return out;
|
|
24
|
-
}
|
|
38
|
+
};
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// This content is autogenerated. Do not edit manually. To override descriptions or summaries, use the doc-tools CLI with the --overrides option: https://redpandadata.atlassian.net/wiki/spaces/DOC/pages/1247543314/Generate+reference+docs+for+Redpanda+Connect
|
|
2
|
-
|
|
3
1
|
{{{summary}}}
|
|
4
2
|
|
|
5
3
|
{{#if version}}
|