@redpanda-data/docs-extensions-and-macros 4.12.2 → 4.12.4
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 +354 -89
- package/extensions/algolia-indexer/generate-index.js +43 -27
- package/package.json +1 -1
- package/tools/property-extractor/Makefile +63 -8
- package/tools/property-extractor/README.adoc +973 -621
- package/tools/property-extractor/compare-properties.js +10 -6
- package/tools/property-extractor/generate-handlebars-docs.js +9 -6
- package/tools/property-extractor/property_extractor.py +93 -10
- package/tools/property-extractor/templates/topic-property-mappings.hbs +6 -1
- package/tools/property-extractor/templates/topic-property.hbs +1 -1
- package/tools/property-extractor/topic_property_extractor.py +269 -65
- package/tools/property-extractor/transformers.py +48 -21
- package/tools/redpanda-connect/README.adoc +736 -0
- package/tools/redpanda-connect/helpers/renderConnectFields.js +57 -31
- package/tools/redpanda-connect/helpers/renderLeafField.js +10 -3
- package/tools/redpanda-connect/helpers/renderYamlList.js +7 -2
- package/tools/redpanda-connect/helpers/toYaml.js +8 -3
- package/tools/redpanda-connect/report-delta.js +64 -2
|
@@ -202,12 +202,16 @@ function compareProperties(oldData, newData, oldVersion, newVersion) {
|
|
|
202
202
|
});
|
|
203
203
|
}
|
|
204
204
|
} else {
|
|
205
|
-
// Property was removed
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
205
|
+
// Property was removed - skip experimental properties
|
|
206
|
+
// Check both the is_experimental_property field and development_ prefix
|
|
207
|
+
const isExperimental = oldProp.is_experimental_property || name.startsWith('development_');
|
|
208
|
+
if (!isExperimental) {
|
|
209
|
+
report.removedProperties.push({
|
|
210
|
+
name,
|
|
211
|
+
type: oldProp.type,
|
|
212
|
+
description: oldProp.description || 'No description'
|
|
213
|
+
});
|
|
214
|
+
}
|
|
211
215
|
}
|
|
212
216
|
}
|
|
213
217
|
|
|
@@ -240,7 +240,7 @@ function generateTopicPropertyMappings(properties, partialsDir) {
|
|
|
240
240
|
throw new Error(`topic-property-mappings.hbs template not found: ${mappingsTemplatePath}`);
|
|
241
241
|
}
|
|
242
242
|
const topicProperties = Object.values(properties).filter(
|
|
243
|
-
p => p.is_topic_property && p.corresponding_cluster_property
|
|
243
|
+
p => p.is_topic_property && p.corresponding_cluster_property && !p.exclude_from_docs
|
|
244
244
|
);
|
|
245
245
|
if (topicProperties.length === 0) {
|
|
246
246
|
console.log('ℹ️ No topic properties with corresponding_cluster_property found. Skipping topic-property-mappings.adoc.');
|
|
@@ -249,7 +249,9 @@ function generateTopicPropertyMappings(properties, partialsDir) {
|
|
|
249
249
|
const hbsSource = fs.readFileSync(mappingsTemplatePath, 'utf8');
|
|
250
250
|
const hbs = handlebars.compile(hbsSource);
|
|
251
251
|
const rendered = hbs({ topicProperties });
|
|
252
|
-
const
|
|
252
|
+
const propertiesPartialsDir = path.join(partialsDir, 'properties');
|
|
253
|
+
fs.mkdirSync(propertiesPartialsDir, { recursive: true });
|
|
254
|
+
const mappingsOut = path.join(propertiesPartialsDir, 'topic-property-mappings.adoc');
|
|
253
255
|
fs.writeFileSync(mappingsOut, AUTOGEN_NOTICE + rendered, 'utf8');
|
|
254
256
|
console.log(`✅ Generated ${mappingsOut}`);
|
|
255
257
|
return topicProperties.length;
|
|
@@ -288,9 +290,9 @@ function generateErrorReports(properties, documentedProperties = []) {
|
|
|
288
290
|
const pctDeprecated = total ? ((deprecatedProperties.length / total) * 100).toFixed(2) : '0.00';
|
|
289
291
|
const pctUndocumented = total ? ((undocumented.length / total) * 100).toFixed(2) : '0.00';
|
|
290
292
|
|
|
291
|
-
console.log(
|
|
292
|
-
console.log(
|
|
293
|
-
console.log(
|
|
293
|
+
console.log(`Empty descriptions: ${emptyDescriptions.length} (${pctEmpty}%) (excludes deprecated)`);
|
|
294
|
+
console.log(`Deprecated: ${deprecatedProperties.length} (${pctDeprecated}%)`);
|
|
295
|
+
console.log(`Not documented: ${undocumented.length} (${pctUndocumented}%)`);
|
|
294
296
|
|
|
295
297
|
return {
|
|
296
298
|
empty_descriptions: emptyDescriptions.sort(),
|
|
@@ -304,7 +306,8 @@ function generateErrorReports(properties, documentedProperties = []) {
|
|
|
304
306
|
*/
|
|
305
307
|
function generateAllDocs(inputFile, outputDir) {
|
|
306
308
|
const data = JSON.parse(fs.readFileSync(inputFile, 'utf8'));
|
|
307
|
-
|
|
309
|
+
// Support both 'properties' (from property_extractor.py) and 'topic_properties' (from topic_property_extractor.py)
|
|
310
|
+
const properties = data.properties || data.topic_properties || {};
|
|
308
311
|
|
|
309
312
|
registerPartials();
|
|
310
313
|
|
|
@@ -748,6 +748,10 @@ def transform_files_with_properties(files_with_properties):
|
|
|
748
748
|
if transformer.accepts(properties[name], fp):
|
|
749
749
|
transformer.parse(property_definition, properties[name], fp)
|
|
750
750
|
|
|
751
|
+
# Skip experimental properties
|
|
752
|
+
if property_definition.get('is_experimental_property'):
|
|
753
|
+
continue
|
|
754
|
+
|
|
751
755
|
if len(property_definition) > 0:
|
|
752
756
|
all_properties[name] = property_definition
|
|
753
757
|
|
|
@@ -787,7 +791,11 @@ def apply_transformers_to_topic_properties(topic_properties):
|
|
|
787
791
|
for transformer in transformers:
|
|
788
792
|
if transformer.accepts(property_definition, mock_fp):
|
|
789
793
|
transformer.parse(property_definition, property_definition, mock_fp)
|
|
790
|
-
|
|
794
|
+
|
|
795
|
+
# Skip experimental properties
|
|
796
|
+
if property_definition.get('is_experimental_property'):
|
|
797
|
+
continue
|
|
798
|
+
|
|
791
799
|
transformed_properties[prop_name] = property_definition
|
|
792
800
|
|
|
793
801
|
logging.info(f"Applied transformers to {len(transformed_properties)} topic properties")
|
|
@@ -2540,22 +2548,28 @@ def resolve_type_and_default(properties, definitions):
|
|
|
2540
2548
|
return properties
|
|
2541
2549
|
|
|
2542
2550
|
|
|
2543
|
-
def extract_topic_properties(source_path):
|
|
2551
|
+
def extract_topic_properties(source_path, cluster_properties=None):
|
|
2544
2552
|
"""
|
|
2545
2553
|
Extract topic properties and convert them to the standard properties format.
|
|
2546
|
-
|
|
2554
|
+
|
|
2547
2555
|
Args:
|
|
2548
2556
|
source_path: Path to the Redpanda source code
|
|
2549
|
-
|
|
2557
|
+
cluster_properties: Optional dictionary of cluster properties for default value lookup
|
|
2558
|
+
|
|
2550
2559
|
Returns:
|
|
2551
2560
|
Dictionary of topic properties in the standard format with config_scope: "topic"
|
|
2552
2561
|
"""
|
|
2553
2562
|
if TopicPropertyExtractor is None:
|
|
2554
2563
|
logging.warning("TopicPropertyExtractor not available, skipping topic property extraction")
|
|
2555
2564
|
return {}
|
|
2556
|
-
|
|
2565
|
+
|
|
2557
2566
|
try:
|
|
2558
|
-
|
|
2567
|
+
# Format cluster properties for TopicPropertyExtractor
|
|
2568
|
+
cluster_props_for_extractor = None
|
|
2569
|
+
if cluster_properties:
|
|
2570
|
+
cluster_props_for_extractor = {"properties": cluster_properties}
|
|
2571
|
+
|
|
2572
|
+
extractor = TopicPropertyExtractor(source_path, cluster_props_for_extractor)
|
|
2559
2573
|
topic_data = extractor.extract_topic_properties()
|
|
2560
2574
|
topic_properties = topic_data.get("topic_properties", {})
|
|
2561
2575
|
|
|
@@ -2616,11 +2630,20 @@ def extract_topic_properties(source_path):
|
|
|
2616
2630
|
"config_scope": "topic",
|
|
2617
2631
|
"defined_in": prop_data.get("defined_in", ""),
|
|
2618
2632
|
"corresponding_cluster_property": prop_data.get("corresponding_cluster_property", ""),
|
|
2633
|
+
"cluster_property_doc_file": prop_data.get("cluster_property_doc_file", ""),
|
|
2634
|
+
"alternate_cluster_property": prop_data.get("alternate_cluster_property", ""),
|
|
2635
|
+
"alternate_cluster_property_doc_file": prop_data.get("alternate_cluster_property_doc_file", ""),
|
|
2619
2636
|
"acceptable_values": prop_data.get("acceptable_values", ""),
|
|
2620
2637
|
"is_deprecated": False,
|
|
2621
2638
|
"is_topic_property": True,
|
|
2622
2639
|
"category": infer_category(prop_name)
|
|
2623
2640
|
}
|
|
2641
|
+
|
|
2642
|
+
# Add default values if they exist (inherited from cluster properties)
|
|
2643
|
+
if "default" in prop_data:
|
|
2644
|
+
converted_properties[prop_name]["default"] = prop_data["default"]
|
|
2645
|
+
if "default_human_readable" in prop_data:
|
|
2646
|
+
converted_properties[prop_name]["default_human_readable"] = prop_data["default_human_readable"]
|
|
2624
2647
|
|
|
2625
2648
|
logging.info(f"Extracted {len(converted_properties)} topic properties (excluding {len([p for p in topic_properties.values() if p.get('is_noop', False)])} no-op properties)")
|
|
2626
2649
|
return converted_properties
|
|
@@ -2809,18 +2832,25 @@ def main():
|
|
|
2809
2832
|
properties = transform_files_with_properties(files_with_properties)
|
|
2810
2833
|
|
|
2811
2834
|
# Extract topic properties and add them to the main properties dictionary
|
|
2812
|
-
|
|
2835
|
+
# Pass cluster properties so topic properties can inherit default values
|
|
2836
|
+
topic_properties = extract_topic_properties(options.path, properties)
|
|
2813
2837
|
if topic_properties:
|
|
2814
2838
|
# Apply transformers to topic properties to ensure they get the same metadata as cluster properties
|
|
2815
2839
|
topic_properties = apply_transformers_to_topic_properties(topic_properties)
|
|
2816
2840
|
properties.update(topic_properties)
|
|
2817
2841
|
logging.info(f"Added {len(topic_properties)} topic properties to the main properties collection")
|
|
2818
2842
|
|
|
2819
|
-
#
|
|
2843
|
+
# Validate and fix up corresponding_cluster_property mappings
|
|
2820
2844
|
# Some cluster properties have a "_default" suffix that the extractor doesn't catch
|
|
2821
2845
|
fixup_count = 0
|
|
2846
|
+
invalid_mappings = []
|
|
2847
|
+
|
|
2822
2848
|
for prop_name, prop_data in properties.items():
|
|
2823
|
-
if prop_data.get('is_topic_property')
|
|
2849
|
+
if not prop_data.get('is_topic_property'):
|
|
2850
|
+
continue
|
|
2851
|
+
|
|
2852
|
+
# Validate primary cluster property mapping
|
|
2853
|
+
if prop_data.get('corresponding_cluster_property'):
|
|
2824
2854
|
cluster_prop = prop_data['corresponding_cluster_property']
|
|
2825
2855
|
# Check if the mapped cluster property exists
|
|
2826
2856
|
if cluster_prop not in properties:
|
|
@@ -2828,10 +2858,63 @@ def main():
|
|
|
2828
2858
|
default_variant = f'{cluster_prop}_default'
|
|
2829
2859
|
if default_variant in properties:
|
|
2830
2860
|
prop_data['corresponding_cluster_property'] = default_variant
|
|
2861
|
+
# Update doc file for the new property name
|
|
2862
|
+
if prop_data.get('cluster_property_doc_file'):
|
|
2863
|
+
# Re-determine doc file for the _default variant
|
|
2864
|
+
if ('cloud_storage' in default_variant or 'remote_' in default_variant or
|
|
2865
|
+
's3_' in default_variant or 'azure_' in default_variant or
|
|
2866
|
+
'gcs_' in default_variant or 'archival_' in default_variant or
|
|
2867
|
+
'tiered_' in default_variant):
|
|
2868
|
+
prop_data['cluster_property_doc_file'] = 'object-storage-properties.adoc'
|
|
2869
|
+
else:
|
|
2870
|
+
prop_data['cluster_property_doc_file'] = 'cluster-properties.adoc'
|
|
2831
2871
|
fixup_count += 1
|
|
2872
|
+
else:
|
|
2873
|
+
invalid_mappings.append({
|
|
2874
|
+
'topic_property': prop_name,
|
|
2875
|
+
'cluster_property': cluster_prop,
|
|
2876
|
+
'type': 'primary'
|
|
2877
|
+
})
|
|
2878
|
+
|
|
2879
|
+
# Validate alternate cluster property mapping (for conditional mappings)
|
|
2880
|
+
if prop_data.get('alternate_cluster_property'):
|
|
2881
|
+
alternate_prop = prop_data['alternate_cluster_property']
|
|
2882
|
+
if alternate_prop not in properties:
|
|
2883
|
+
# Try the _default variant
|
|
2884
|
+
default_variant = f'{alternate_prop}_default'
|
|
2885
|
+
if default_variant in properties:
|
|
2886
|
+
prop_data['alternate_cluster_property'] = default_variant
|
|
2887
|
+
# Update doc file for the new property name
|
|
2888
|
+
if prop_data.get('alternate_cluster_property_doc_file'):
|
|
2889
|
+
if ('cloud_storage' in default_variant or 'remote_' in default_variant or
|
|
2890
|
+
's3_' in default_variant or 'azure_' in default_variant or
|
|
2891
|
+
'gcs_' in default_variant or 'archival_' in default_variant or
|
|
2892
|
+
'tiered_' in default_variant):
|
|
2893
|
+
prop_data['alternate_cluster_property_doc_file'] = 'object-storage-properties.adoc'
|
|
2894
|
+
else:
|
|
2895
|
+
prop_data['alternate_cluster_property_doc_file'] = 'cluster-properties.adoc'
|
|
2896
|
+
fixup_count += 1
|
|
2897
|
+
else:
|
|
2898
|
+
invalid_mappings.append({
|
|
2899
|
+
'topic_property': prop_name,
|
|
2900
|
+
'cluster_property': alternate_prop,
|
|
2901
|
+
'type': 'alternate'
|
|
2902
|
+
})
|
|
2832
2903
|
|
|
2833
2904
|
if fixup_count > 0:
|
|
2834
|
-
|
|
2905
|
+
print(f"✅ Fixed {fixup_count} cluster property mappings by adding '_default' suffix", file=sys.stderr, flush=True)
|
|
2906
|
+
|
|
2907
|
+
# Report invalid mappings
|
|
2908
|
+
if invalid_mappings:
|
|
2909
|
+
print(f"\n⚠️ Found {len(invalid_mappings)} topic properties with invalid cluster property mappings:", file=sys.stderr, flush=True)
|
|
2910
|
+
for mapping in invalid_mappings:
|
|
2911
|
+
print(f" • {mapping['topic_property']} -> {mapping['cluster_property']} ({mapping['type']}) [CLUSTER PROPERTY NOT FOUND]", file=sys.stderr, flush=True)
|
|
2912
|
+
print("These mappings reference cluster properties that do not exist in the extracted properties.", file=sys.stderr, flush=True)
|
|
2913
|
+
print("This could indicate:", file=sys.stderr, flush=True)
|
|
2914
|
+
print(" 1. The cluster property name changed in the source code", file=sys.stderr, flush=True)
|
|
2915
|
+
print(" 2. The cluster property is not being extracted properly", file=sys.stderr, flush=True)
|
|
2916
|
+
print(" 3. The mapping logic in config_response_utils.cc uses a computed value, not a real cluster property", file=sys.stderr, flush=True)
|
|
2917
|
+
print("", file=sys.stderr, flush=True)
|
|
2835
2918
|
|
|
2836
2919
|
# First, create the original properties without overrides for the base JSON output
|
|
2837
2920
|
# 1. Add config_scope field based on which source file defines the property
|
|
@@ -4,8 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
{{#each topicProperties}}
|
|
6
6
|
| <<{{anchorName name}},`{{name}}`>>
|
|
7
|
-
|
|
|
7
|
+
| {{#if alternate_cluster_property~}}
|
|
8
|
+
xref:./{{cluster_property_doc_file}}#{{corresponding_cluster_property}}[`{{corresponding_cluster_property}}`] or xref:./{{alternate_cluster_property_doc_file}}#{{alternate_cluster_property}}[`{{alternate_cluster_property}}`]
|
|
9
|
+
{{~else~}}
|
|
10
|
+
xref:./{{cluster_property_doc_file}}#{{corresponding_cluster_property}}[`{{corresponding_cluster_property}}`]
|
|
11
|
+
{{~/if}}
|
|
8
12
|
{{/each}}
|
|
13
|
+
|
|
9
14
|
|===
|
|
10
15
|
|
|
11
16
|
{{!-- Helper to generate anchor names --}}
|
|
@@ -65,7 +65,7 @@ endif::[]
|
|
|
65
65
|
{{/if}}{{/if}}{{/if}}
|
|
66
66
|
{{#if corresponding_cluster_property}}
|
|
67
67
|
|
|
68
|
-
|
|
|
68
|
+
| Corresponding cluster property
|
|
69
69
|
| xref:reference:cluster-properties.adoc#{{corresponding_cluster_property}}[{{corresponding_cluster_property}}]
|
|
70
70
|
{{/if}}
|
|
71
71
|
{{#if (and minimum maximum)}}
|