|
69 | 69 | from property_bag import PropertyBag |
70 | 70 | from transformers import * |
71 | 71 |
|
| 72 | +# Import topic property extractor |
| 73 | +try: |
| 74 | + from topic_property_extractor import TopicPropertyExtractor |
| 75 | +except ImportError: |
| 76 | + # TopicPropertyExtractor not available, will skip topic property extraction |
| 77 | + TopicPropertyExtractor = None |
| 78 | + |
72 | 79 | logger = logging.getLogger("viewer") |
73 | 80 |
|
74 | 81 |
|
@@ -484,18 +491,23 @@ def _process_example_override(override, overrides_file_path=None): |
484 | 491 |
|
485 | 492 | def add_config_scope(properties): |
486 | 493 | """ |
487 | | - Add a config_scope field to each property based on its defined_in value. |
| 494 | + Add a config_scope field to each property based on its defined_in value or property type. |
488 | 495 | 'cluster' if defined_in == src/v/config/configuration.cc |
489 | 496 | 'broker' if defined_in == src/v/config/node_config.cc |
| 497 | + 'topic' if is_topic_property == True |
490 | 498 | """ |
491 | 499 | for prop in properties.values(): |
492 | | - defined_in = prop.get("defined_in", "") |
493 | | - if defined_in == "src/v/config/configuration.cc": |
494 | | - prop["config_scope"] = "cluster" |
495 | | - elif defined_in == "src/v/config/node_config.cc": |
496 | | - prop["config_scope"] = "broker" |
| 500 | + # Check if this is a topic property first |
| 501 | + if prop.get("is_topic_property", False): |
| 502 | + prop["config_scope"] = "topic" |
497 | 503 | else: |
498 | | - prop["config_scope"] = None |
| 504 | + defined_in = prop.get("defined_in", "") |
| 505 | + if defined_in == "src/v/config/configuration.cc": |
| 506 | + prop["config_scope"] = "cluster" |
| 507 | + elif defined_in == "src/v/config/node_config.cc": |
| 508 | + prop["config_scope"] = "broker" |
| 509 | + else: |
| 510 | + prop["config_scope"] = None |
499 | 511 | return properties |
500 | 512 |
|
501 | 513 |
|
@@ -1159,6 +1171,52 @@ def expand_default(type_name, default_str): |
1159 | 1171 | return properties |
1160 | 1172 |
|
1161 | 1173 |
|
| 1174 | +def extract_topic_properties(source_path): |
| 1175 | + """ |
| 1176 | + Extract topic properties and convert them to the standard properties format. |
| 1177 | + |
| 1178 | + Args: |
| 1179 | + source_path: Path to the Redpanda source code |
| 1180 | + |
| 1181 | + Returns: |
| 1182 | + Dictionary of topic properties in the standard format with config_scope: "topic" |
| 1183 | + """ |
| 1184 | + if TopicPropertyExtractor is None: |
| 1185 | + logging.warning("TopicPropertyExtractor not available, skipping topic property extraction") |
| 1186 | + return {} |
| 1187 | + |
| 1188 | + try: |
| 1189 | + extractor = TopicPropertyExtractor(source_path) |
| 1190 | + topic_data = extractor.extract_topic_properties() |
| 1191 | + topic_properties = topic_data.get("topic_properties", {}) |
| 1192 | + |
| 1193 | + # Convert topic properties to the standard properties format |
| 1194 | + converted_properties = {} |
| 1195 | + for prop_name, prop_data in topic_properties.items(): |
| 1196 | + # Skip no-op properties |
| 1197 | + if prop_data.get("is_noop", False): |
| 1198 | + continue |
| 1199 | + |
| 1200 | + converted_properties[prop_name] = { |
| 1201 | + "name": prop_name, |
| 1202 | + "description": prop_data.get("description", ""), |
| 1203 | + "type": prop_data.get("type", "string"), |
| 1204 | + "config_scope": "topic", |
| 1205 | + "source_file": prop_data.get("source_file", ""), |
| 1206 | + "corresponding_cluster_property": prop_data.get("corresponding_cluster_property", ""), |
| 1207 | + "acceptable_values": prop_data.get("acceptable_values", ""), |
| 1208 | + "is_deprecated": False, |
| 1209 | + "is_topic_property": True |
| 1210 | + } |
| 1211 | + |
| 1212 | + 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)") |
| 1213 | + return converted_properties |
| 1214 | + |
| 1215 | + except Exception as e: |
| 1216 | + logging.error(f"Failed to extract topic properties: {e}") |
| 1217 | + return {} |
| 1218 | + |
| 1219 | + |
1162 | 1220 | def main(): |
1163 | 1221 | import argparse |
1164 | 1222 |
|
@@ -1263,6 +1321,12 @@ def generate_options(): |
1263 | 1321 | ) |
1264 | 1322 | properties = transform_files_with_properties(files_with_properties) |
1265 | 1323 |
|
| 1324 | + # Extract topic properties and add them to the main properties dictionary |
| 1325 | + topic_properties = extract_topic_properties(options.path) |
| 1326 | + if topic_properties: |
| 1327 | + properties.update(topic_properties) |
| 1328 | + logging.info(f"Added {len(topic_properties)} topic properties to the main properties collection") |
| 1329 | + |
1266 | 1330 | # First, create the original properties without overrides for the base JSON output |
1267 | 1331 | # 1. Add config_scope field based on which source file defines the property |
1268 | 1332 | original_properties = add_config_scope(deepcopy(properties)) |
|
0 commit comments