@redpanda-data/docs-extensions-and-macros 4.12.2 → 4.12.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.
@@ -202,12 +202,16 @@ function compareProperties(oldData, newData, oldVersion, newVersion) {
202
202
  });
203
203
  }
204
204
  } else {
205
- // Property was removed
206
- report.removedProperties.push({
207
- name,
208
- type: oldProp.type,
209
- description: oldProp.description || 'No description'
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 mappingsOut = path.join(partialsDir, 'topic-property-mappings.adoc');
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(`📉 Empty descriptions: ${emptyDescriptions.length} (${pctEmpty}%) (excludes deprecated)`);
292
- console.log(`🕸️ Deprecated: ${deprecatedProperties.length} (${pctDeprecated}%)`);
293
- console.log(`🚫 Not documented: ${undocumented.length} (${pctUndocumented}%)`);
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
- const properties = data.properties || {};
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
- extractor = TopicPropertyExtractor(source_path)
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
- topic_properties = extract_topic_properties(options.path)
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
- # Fix up corresponding_cluster_property mappings
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') and prop_data.get('corresponding_cluster_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
- logging.info(f"Fixed {fixup_count} cluster property mappings by adding '_default' suffix")
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
- | xref:./cluster-properties.adoc#{{corresponding_cluster_property}}[`{{corresponding_cluster_property}}`]
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
- | Related cluster property
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)}}