@redpanda-data/docs-extensions-and-macros 4.7.4 → 4.8.1

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.
Files changed (26) hide show
  1. package/bin/doc-tools.js +90 -20
  2. package/package.json +1 -1
  3. package/tools/property-extractor/Makefile +64 -24
  4. package/tools/property-extractor/generate-handlebars-docs.js +344 -0
  5. package/tools/property-extractor/helpers/and.js +10 -0
  6. package/tools/property-extractor/helpers/eq.js +9 -0
  7. package/tools/property-extractor/helpers/formatPropertyValue.js +128 -0
  8. package/tools/property-extractor/helpers/formatUnits.js +26 -0
  9. package/tools/property-extractor/helpers/index.js +13 -0
  10. package/tools/property-extractor/helpers/join.js +18 -0
  11. package/tools/property-extractor/helpers/ne.js +9 -0
  12. package/tools/property-extractor/helpers/not.js +8 -0
  13. package/tools/property-extractor/helpers/or.js +10 -0
  14. package/tools/property-extractor/helpers/renderPropertyExample.js +42 -0
  15. package/tools/property-extractor/package-lock.json +77 -0
  16. package/tools/property-extractor/package.json +6 -0
  17. package/tools/property-extractor/property_extractor.py +1163 -20
  18. package/tools/property-extractor/requirements.txt +1 -0
  19. package/tools/property-extractor/templates/deprecated-properties.hbs +25 -0
  20. package/tools/property-extractor/templates/deprecated-property.hbs +7 -0
  21. package/tools/property-extractor/templates/property-page.hbs +22 -0
  22. package/tools/property-extractor/templates/property.hbs +70 -0
  23. package/tools/property-extractor/templates/topic-property.hbs +59 -0
  24. package/tools/property-extractor/topic_property_extractor.py +630 -0
  25. package/tools/property-extractor/transformers.py +80 -4
  26. package/tools/property-extractor/json-to-asciidoc/generate_docs.py +0 -466
@@ -36,13 +36,52 @@ class IsNullableTransformer:
36
36
 
37
37
 
38
38
  class IsArrayTransformer:
39
+ """
40
+ Detects properties that should be treated as arrays based on their C++ type declarations.
41
+
42
+ This transformer identifies two types of array properties:
43
+ 1. std::vector<T> - Standard C++ vectors
44
+ 2. one_or_many_property<T> - Redpanda's custom type that accepts either a single value or an array
45
+
46
+ The one_or_many_property type is used in Redpanda configuration for properties like 'admin'
47
+ and 'admin_api_tls' where users can specify either:
48
+ - A single object: admin: {address: "127.0.0.1", port: 9644}
49
+ - An array of objects: admin: [{address: "127.0.0.1", port: 9644}, {address: "0.0.0.0", port: 9645}]
50
+
51
+ When detected, these properties are marked with:
52
+ - type: "array"
53
+ - items: {type: <inner_type>} where <inner_type> is extracted from T
54
+ """
55
+
56
+ # Class-level constants for array type patterns
57
+ ARRAY_PATTERN_STD_VECTOR = "std::vector"
58
+ ARRAY_PATTERN_ONE_OR_MANY = "one_or_many_property"
59
+
39
60
  def __init__(self, type_transformer):
40
61
  self.type_transformer = type_transformer
41
62
 
42
63
  def accepts(self, info, file_pair):
43
- return "std::vector" in info["declaration"]
64
+ """
65
+ Check if this property declaration represents an array type.
66
+
67
+ Returns True for:
68
+ - std::vector<T> declarations (standard C++ vectors)
69
+ - one_or_many_property<T> declarations (Redpanda's flexible array type)
70
+ """
71
+ return (self.ARRAY_PATTERN_STD_VECTOR in info["declaration"] or
72
+ self.ARRAY_PATTERN_ONE_OR_MANY in info["declaration"])
44
73
 
45
74
  def parse(self, property, info, file_pair):
75
+ """
76
+ Transform the property to indicate it's an array type.
77
+
78
+ Sets:
79
+ - property["type"] = "array"
80
+ - property["items"]["type"] = <extracted_inner_type>
81
+
82
+ The inner type is extracted by the type_transformer, which handles
83
+ removing the wrapper (std::vector<> or one_or_many_property<>) to get T.
84
+ """
46
85
  property["type"] = "array"
47
86
  property["items"] = PropertyBag()
48
87
  property["items"]["type"] = self.type_transformer.get_type_from_declaration(
@@ -94,10 +133,35 @@ class VisibilityTransformer:
94
133
 
95
134
 
96
135
  class TypeTransformer:
136
+
137
+ # Class-level constants for type pattern matching
138
+ # Shared with IsArrayTransformer for consistency
139
+ ARRAY_PATTERN_STD_VECTOR = "std::vector"
140
+ ARRAY_PATTERN_ONE_OR_MANY = "one_or_many_property"
141
+ OPTIONAL_PATTERN = "std::optional"
142
+
97
143
  def accepts(self, info, file_pair):
98
144
  return True
99
145
 
100
146
  def get_cpp_type_from_declaration(self, declaration):
147
+ """
148
+ Extract the inner type from C++ property declarations.
149
+
150
+ This method handles various C++ template types and extracts the core type T from:
151
+ - property<T> -> T
152
+ - std::optional<T> -> T
153
+ - std::vector<T> -> T
154
+ - one_or_many_property<T> -> T (Redpanda's flexible array type)
155
+
156
+ For one_or_many_property, this is crucial because it allows the same property
157
+ to accept either a single value or an array of values in the configuration.
158
+ Examples:
159
+ - one_or_many_property<model::broker_endpoint> -> model::broker_endpoint
160
+ - one_or_many_property<endpoint_tls_config> -> endpoint_tls_config
161
+
162
+ The extracted type is then used to determine the JSON schema type and
163
+ for resolving default values from the definitions.
164
+ """
101
165
  one_line_declaration = declaration.replace("\n", "").strip()
102
166
  raw_type = (
103
167
  re.sub(r"^.*property<(.+)>.*", "\\1", one_line_declaration)
@@ -105,11 +169,19 @@ class TypeTransformer:
105
169
  .replace(",", "")
106
170
  )
107
171
 
108
- if "std::optional" in raw_type:
172
+ if self.OPTIONAL_PATTERN in raw_type:
109
173
  raw_type = re.sub(".*std::optional<(.+)>.*", "\\1", raw_type)
110
174
 
111
- if "std::vector" in raw_type:
175
+ if self.ARRAY_PATTERN_STD_VECTOR in raw_type:
112
176
  raw_type = re.sub(".*std::vector<(.+)>.*", "\\1", raw_type)
177
+
178
+ # Handle one_or_many_property<T> - extract the inner type T
179
+ # This is essential for Redpanda's flexible configuration properties
180
+ # that can accept either single values or arrays
181
+ # Check and extract from raw_type for consistency with other type extractors
182
+ if self.ARRAY_PATTERN_ONE_OR_MANY in raw_type:
183
+ raw_type = re.sub(".*one_or_many_property<(.+)>.*", "\\1", raw_type)
184
+ raw_type = raw_type.split()[0].replace(",", "")
113
185
 
114
186
  return raw_type
115
187
 
@@ -282,6 +354,10 @@ class FriendlyDefaultTransformer:
282
354
  - std::chrono::milliseconds(10)
283
355
  - std::nullopt
284
356
  """
357
+
358
+ # Class-level constants for pattern matching in default values
359
+ ARRAY_PATTERN_STD_VECTOR = "std::vector"
360
+
285
361
  def accepts(self, info, file_pair):
286
362
  return info.get("params") and len(info["params"]) > 3
287
363
 
@@ -308,7 +384,7 @@ class FriendlyDefaultTransformer:
308
384
  return property
309
385
 
310
386
  # Transform std::vector defaults.
311
- if "std::vector" in default:
387
+ if self.ARRAY_PATTERN_STD_VECTOR in default:
312
388
  m = re.search(r'\{([^}]+)\}', default)
313
389
  if m:
314
390
  contents = m.group(1).strip()
@@ -1,466 +0,0 @@
1
- import json
2
- import os
3
- import re
4
- import argparse
5
-
6
- # --- Constants for Paths and Filenames ---
7
- INPUT_JSON_PATH = "gen/"
8
- INPUT_JSON_FILE = "properties-output.json"
9
-
10
- OUTPUT_DIR_DEFAULT = "output"
11
- PAGE_FOLDER_NAME = "pages"
12
- ERROR_FOLDER_NAME = "error"
13
-
14
- OUTPUT_FILE_BROKER = "broker-properties.adoc"
15
- OUTPUT_FILE_CLUSTER = "cluster-properties.adoc"
16
- OUTPUT_FILE_CLOUD = "object-storage-properties.adoc"
17
- OUTPUT_FILE_DEPRECATED = os.path.join("deprecated", "partials", "deprecated-properties.adoc")
18
- ALL_PROPERTIES_FILE = "all_properties.txt"
19
-
20
- ERROR_FILE_DESCRIPTION = "empty_description.txt"
21
- ERROR_FILE_TYPE = "empty_type.txt"
22
- ERROR_FILE_MAX_WITHOUT_MIN = "max_without_min.txt"
23
- ERROR_FILE_MIN_WITHOUT_MAX = "min_without_max.txt"
24
-
25
- # --- Static Documentation Strings ---
26
- BROKER_PAGE_TITLE = (
27
- "= Broker Configuration Properties\n"
28
- ":page-aliases: reference:node-properties.adoc, reference:node-configuration-sample.adoc\n"
29
- ":description: Reference of broker configuration properties.\n\n"
30
- )
31
- BROKER_INTRO = (
32
- "Broker configuration properties are applied individually to each broker in a cluster. "
33
- "You can find and modify these properties in the `redpanda.yaml` configuration file.\n\n"
34
- "For information on how to edit broker properties, see xref:manage:cluster-maintenance/node-property-configuration.adoc[].\n\n"
35
- "NOTE: All broker properties require that you restart Redpanda for any update to take effect.\n\n"
36
- )
37
- BROKER_TITLE = "== Broker configuration\n\n"
38
-
39
- SCHEMA_REGISTRY_TITLE = "== Schema Registry\n\n"
40
- PANDAPROXY_TITLE = "== HTTP Proxy\n\n"
41
- KAFKA_CLIENT_TITLE = "== HTTP Proxy Client\n\n"
42
-
43
- SCHEMA_REGISTRY_INTRO = (
44
- "The Schema Registry provides configuration properties to help you enable producers and consumers "
45
- "to share information needed to serialize and deserialize producer and consumer messages.\n\n"
46
- "For information on how to edit broker properties for the Schema Registry, see xref:manage:cluster-maintenance/node-property-configuration.adoc[].\n\n"
47
- )
48
- PANDAPROXY_INTRO = (
49
- "Redpanda HTTP Proxy allows access to your data through a REST API. For example, you can list topics or brokers, "
50
- "get events, produce events, subscribe to events from topics using consumer groups, and commit offsets for a consumer.\n\n"
51
- "See xref:develop:http-proxy.adoc[]\n\n"
52
- )
53
- KAFKA_CLIENT_INTRO = "Configuration options for HTTP Proxy Client.\n\n"
54
-
55
- CLUSTER_PAGE_TITLE = (
56
- "= Cluster Configuration Properties\n"
57
- ":page-aliases: reference:tunable-properties.adoc, reference:cluster-properties.adoc\n"
58
- ":description: Cluster configuration properties list.\n\n"
59
- )
60
- CLUSTER_CONFIG_INTRO = (
61
- "Cluster configuration properties are the same for all brokers in a cluster, and are set at the cluster level.\n\n"
62
- "For information on how to edit cluster properties, see xref:manage:cluster-maintenance/cluster-property-configuration.adoc[] "
63
- "or xref:manage:kubernetes/k-cluster-property-configuration.adoc[].\n\n"
64
- "NOTE: Some cluster properties require that you restart the cluster for any updates to take effect. "
65
- "See the specific property details to identify whether or not a restart is required.\n\n"
66
- )
67
- CLUSTER_CONFIG_TITLE = "== Cluster configuration\n\n"
68
-
69
- CLOUD_PAGE_TITLE = (
70
- "= Object Storage Properties\n"
71
- ":description: Reference of object storage properties.\n\n"
72
- )
73
- CLOUD_CONFIG_INTRO = (
74
- "Object storage properties are a type of cluster property. For information on how to edit cluster properties, "
75
- "see xref:manage:cluster-maintenance/cluster-property-configuration.adoc[].\n\n"
76
- "NOTE: Some object storage properties require that you restart the cluster for any updates to take effect. "
77
- "See the specific property details to identify whether or not a restart is required.\n\n"
78
- )
79
- CLOUD_CONFIG_TITLE = (
80
- "== Object storage configuration\n\n"
81
- "Object storage properties should only be set if you enable xref:manage:tiered-storage.adoc[Tiered Storage].\n\n"
82
- )
83
-
84
- DEPRECATED_PROPERTIES_TITLE = "\n== Configuration properties\n\n"
85
- DEPRECATED_PROPERTIES_INTRO = "This is an exhaustive list of all the deprecated properties.\n\n"
86
- DEPRECATED_BROKER_TITLE = "=== Broker properties\n\n"
87
- DEPRECATED_CLUSTER_TITLE = "=== Cluster properties\n\n"
88
-
89
- # --- Mapping Constants ---
90
- DEFINED_IN_MAPPING = {
91
- "src/v/config/node_config.cc": "broker",
92
- "src/v/pandaproxy/schema_registry/configuration.cc": "schema reg",
93
- "src/v/pandaproxy/rest/configuration.cc": "http proxy",
94
- "src/v/kafka/client/configuration.cc": "http client",
95
- "src/v/config/configuration.cc": "cluster"
96
- }
97
-
98
- SUFFIX_TO_UNIT = {
99
- "ms": "milliseconds",
100
- "sec": "seconds", # Code is not always consistent when using seconds.
101
- "seconds": "seconds",
102
- "bytes": "bytes",
103
- "buf": "bytes",
104
- "partitions": "number of partitions per topic",
105
- "percent": "percent",
106
- "bps": "bytes per second",
107
- "fraction": "fraction"
108
- }
109
-
110
- # --- Utility Functions ---
111
- def parse_arguments():
112
- parser = argparse.ArgumentParser(
113
- description="Generate documentation from properties JSON"
114
- )
115
- parser.add_argument(
116
- "--output-dir",
117
- type=str,
118
- required=True,
119
- help="Directory to save the generated documentation",
120
- )
121
- return parser.parse_args()
122
-
123
- def ensure_directory_exists(directory):
124
- os.makedirs(directory, exist_ok=True)
125
-
126
- def load_json(input_path, input_file):
127
- try:
128
- with open(os.path.join(input_path, input_file), "r", encoding="utf-8") as json_file:
129
- return json.load(json_file)
130
- except FileNotFoundError:
131
- print(f"Error: The file '{input_file}' does not exist.")
132
- return {}
133
- except json.JSONDecodeError as e:
134
- print(f"Error: Failed to parse JSON in '{input_file}': {str(e)}")
135
- return {}
136
-
137
- def process_defaults(input_string, suffix):
138
- # Test for ip:port in vector
139
- vector_match = re.search(
140
- r'std::vector<net::unresolved_address>\(\{\{("([\d.]+)",\s*(\d+))\}\}\)', input_string
141
- )
142
- if vector_match:
143
- ip = vector_match.group(2)
144
- port = vector_match.group(3)
145
- return [f"{ip}:{port}"]
146
-
147
- # Test for ip:port in single-string
148
- broker_match = re.search(r'net::unresolved_address\("([\d.]+)",\s*(\d+)\)', input_string)
149
- if broker_match:
150
- ip = broker_match.group(1)
151
- port = broker_match.group(2)
152
- return f"{ip}:{port}"
153
-
154
- # Handle single time units: seconds, milliseconds, hours, minutes
155
- time_match = re.search(r"(\d+)(ms|s|min|h)", input_string)
156
- # Handle complex time expressions like '24h*365'
157
- complex_match = re.search(r"(\d+)(h|min|s|ms)\s*\*\s*(\d+)", input_string)
158
- # Handle std::chrono::time expressions
159
- chrono_match = re.search(r"std::chrono::(\w+)[\{\(](\d+)[\)\}]", input_string)
160
-
161
- if time_match:
162
- value = int(time_match.group(1))
163
- unit = time_match.group(2)
164
- if suffix == "ms":
165
- if unit == "ms":
166
- return value
167
- elif unit == "s":
168
- return value * 1000
169
- elif unit == "min":
170
- return value * 60 * 1000
171
- elif unit == "h":
172
- return value * 60 * 60 * 1000
173
- elif suffix == "sec":
174
- if unit == "s":
175
- return value
176
- elif unit == "min":
177
- return value * 60
178
- elif unit == "h":
179
- return value * 60 * 60
180
- elif unit == "ms":
181
- return value / 1000
182
-
183
- if complex_match:
184
- value = int(complex_match.group(1))
185
- unit = complex_match.group(2)
186
- multiplier = int(complex_match.group(3))
187
- if suffix == "ms":
188
- if unit == "h":
189
- return value * 60 * 60 * 1000 * multiplier
190
- elif unit == "min":
191
- return value * 60 * 1000 * multiplier
192
- elif unit == "s":
193
- return value * 1000 * multiplier
194
- elif unit == "ms":
195
- return value * multiplier
196
- elif suffix == "sec":
197
- if unit == "h":
198
- return value * 60 * 60 * multiplier
199
- elif unit == "min":
200
- return value * 60 * multiplier
201
- elif unit == "s":
202
- return value * multiplier
203
- elif unit == "ms":
204
- return (value * multiplier) / 1000
205
-
206
- if chrono_match:
207
- chrono_unit = chrono_match.group(1)
208
- chrono_value = int(chrono_match.group(2))
209
- chrono_conversion = {
210
- "milliseconds": 1,
211
- "seconds": 1000,
212
- "minutes": 60 * 1000,
213
- "hours": 60 * 60 * 1000,
214
- "days": 24 * 60 * 60 * 1000,
215
- "weeks": 7 * 24 * 60 * 60 * 1000,
216
- }
217
- if suffix == "ms":
218
- return chrono_value * chrono_conversion.get(chrono_unit, 1)
219
- elif suffix == "sec":
220
- if chrono_unit == "milliseconds":
221
- return chrono_value / 1000
222
- else:
223
- return (chrono_value * chrono_conversion.get(chrono_unit, 1)) / 1000
224
-
225
- # Return the original string if no pattern matches
226
- return input_string
227
-
228
- def generate_property_doc(key, value):
229
- """
230
- Generate documentation string for a single property.
231
- Returns None if required fields are missing.
232
- """
233
- description = value.get("description", "").strip()
234
- prop_type = value.get("type", "").strip()
235
- if not description or not prop_type:
236
- return None
237
-
238
- # Capitalize first letter and ensure a period at the end.
239
- description = description[0].upper() + description[1:]
240
- if not description.endswith('.'):
241
- description += '.'
242
-
243
- lines = [f"=== {value.get('name')}\n\n", f"{description}\n\n"]
244
-
245
- property_suffix = value.get("name").split('_')[-1]
246
- if property_suffix in SUFFIX_TO_UNIT:
247
- lines.append(f"*Unit:* {SUFFIX_TO_UNIT[property_suffix]}\n\n")
248
-
249
- # For non-broker properties (node_config.cc indicates broker), add restart info.
250
- if value.get("defined_in") != "src/v/config/node_config.cc":
251
- restart = "Yes" if value.get("needs_restart", False) else "No"
252
- lines.append(f"*Requires restart:* {restart}\n\n")
253
-
254
- if "gets_restored" in value:
255
- restored = "Yes" if value.get("gets_restored", False) else "No"
256
- lines.append(f"*Gets restored during cluster restore:* {restored}\n\n")
257
-
258
- visibility = value.get("visibility") or "user"
259
- lines.append(f"*Visibility:* `{visibility}`\n\n")
260
-
261
- if prop_type in ["string", "array", "number", "boolean", "integer"]:
262
- lines.append(f"*Type:* {prop_type}\n\n")
263
-
264
- # Add aliases if they exist
265
- aliases = value.get("aliases")
266
- if aliases and len(aliases) > 0:
267
- aliases_str = ", ".join(f"`{alias}`" for alias in aliases)
268
- lines.append(f"*Aliases:* {aliases_str}\n\n")
269
-
270
- if value.get("maximum") is not None and value.get("minimum") is not None:
271
- lines.append(
272
- f"*Accepted values:* [`{value.get('minimum')}`, `{value.get('maximum')}`]\n\n"
273
- )
274
-
275
- default = value.get("default")
276
- if default is None or default == "":
277
- default_str = "null"
278
- elif isinstance(default, bool):
279
- default_str = "true" if default else "false"
280
- else:
281
- default_str = str(default).replace("'", "").lower()
282
- default_str = process_defaults(default_str, property_suffix)
283
- lines.append(f"*Default:* `{default_str}`\n\n")
284
- lines.append("---\n\n")
285
- return "".join(lines)
286
-
287
- def write_data_to_file(output_dir, filename, data):
288
- file_path = os.path.join(output_dir, filename)
289
- ensure_directory_exists(os.path.dirname(file_path))
290
- try:
291
- with open(file_path, "w+", encoding="utf-8") as output:
292
- output.write(data)
293
- print(f"Data written to {file_path} successfully.")
294
- return True
295
- except Exception as e:
296
- print(f"Error writing data to {filename}: {str(e)}")
297
- return False
298
-
299
- def write_error_file(output_dir, filename, error_content, total_properties):
300
- file_path = os.path.join(output_dir, filename)
301
- ensure_directory_exists(os.path.dirname(file_path))
302
- try:
303
- if os.path.exists(file_path):
304
- os.remove(file_path)
305
- if error_content:
306
- error_content = error_content.rstrip("\n")
307
- with open(file_path, "w+", encoding="utf-8") as output:
308
- output.write(error_content)
309
- error_count = len(error_content.split("\n"))
310
- if error_count > 0:
311
- empty_name = filename.replace("empty_", "").replace(".txt", "")
312
- error_type = (
313
- "deprecated properties"
314
- if empty_name == "deprecated_properties"
315
- else f"properties with empty {empty_name}"
316
- )
317
- error_percentage = round((error_count / total_properties) * 100, 2)
318
- print(
319
- f"You have {error_count} {error_type}. Percentage of errors: {error_percentage}%. Data written in '{filename}'."
320
- )
321
- except Exception as e:
322
- print(f"Error writing error data to '{filename}': {str(e)}")
323
-
324
- # --- Main Processing ---
325
- def main():
326
- args = parse_arguments()
327
- output_dir = args.output_dir
328
- page_folder = os.path.join(output_dir, PAGE_FOLDER_NAME)
329
- error_folder = os.path.join(output_dir, ERROR_FOLDER_NAME)
330
-
331
- data = load_json(INPUT_JSON_PATH, INPUT_JSON_FILE)
332
- properties = data.get("properties", {})
333
- total_properties = len(properties)
334
-
335
- # Accumulators for property documentation and error logs.
336
- broker_config_content = []
337
- schema_registry_content = []
338
- pandaproxy_content = []
339
- kafka_client_content = []
340
- cluster_config_content = []
341
- cloud_config_content = []
342
- deprecated_broker_content = []
343
- deprecated_cluster_content = []
344
- all_properties = []
345
- empty_description_errors = []
346
- empty_type_errors = []
347
- max_without_min_errors = []
348
- min_without_max_errors = []
349
- deprecated_properties_errors = []
350
-
351
- for key, value in properties.items():
352
- all_properties.append(key)
353
- group = None
354
- if key.startswith("cloud_"):
355
- group = "cloud"
356
- else:
357
- group = DEFINED_IN_MAPPING.get(value.get("defined_in"))
358
-
359
- # Handle deprecated properties.
360
- if value.get("is_deprecated") is True:
361
- deprecated_properties_errors.append(key)
362
- if group == "broker":
363
- deprecated_broker_content.append(f"- {key}\n\n")
364
- elif group in ["cluster", "cloud"]:
365
- deprecated_cluster_content.append(f"- {key}\n\n")
366
- continue
367
-
368
- # Log errors for missing description or type.
369
- if not value.get("description", "").strip():
370
- empty_description_errors.append(key)
371
- if not value.get("type", "").strip():
372
- empty_type_errors.append(key)
373
-
374
- # Check for max/min inconsistencies.
375
- if value.get("maximum") is not None and value.get("minimum") is None:
376
- max_without_min_errors.append(key)
377
- if value.get("minimum") is not None and value.get("maximum") is None:
378
- min_without_max_errors.append(key)
379
-
380
- property_doc = generate_property_doc(key, value)
381
- if property_doc is None:
382
- continue
383
-
384
- group_mapping = {
385
- "broker": broker_config_content,
386
- "schema reg": schema_registry_content,
387
- "http proxy": pandaproxy_content,
388
- "http client": kafka_client_content,
389
- "cluster": cluster_config_content,
390
- "cloud": cloud_config_content,
391
- }
392
- if group in group_mapping:
393
- group_mapping[group].append(property_doc)
394
-
395
- # Construct final documentation pages.
396
- broker_page = (
397
- BROKER_PAGE_TITLE
398
- + BROKER_INTRO
399
- + BROKER_TITLE
400
- + "".join(broker_config_content)
401
- + "\n\n"
402
- + SCHEMA_REGISTRY_TITLE
403
- + SCHEMA_REGISTRY_INTRO
404
- + "".join(schema_registry_content)
405
- + "\n\n"
406
- + PANDAPROXY_TITLE
407
- + PANDAPROXY_INTRO
408
- + "".join(pandaproxy_content)
409
- + "\n\n"
410
- + KAFKA_CLIENT_TITLE
411
- + KAFKA_CLIENT_INTRO
412
- + "".join(kafka_client_content)
413
- )
414
- cluster_page = (
415
- CLUSTER_PAGE_TITLE
416
- + CLUSTER_CONFIG_INTRO
417
- + CLUSTER_CONFIG_TITLE
418
- + "".join(cluster_config_content)
419
- )
420
- cloud_page = (
421
- CLOUD_PAGE_TITLE
422
- + CLOUD_CONFIG_INTRO
423
- + CLOUD_CONFIG_TITLE
424
- + "".join(cloud_config_content)
425
- )
426
- deprecated_page = (
427
- DEPRECATED_PROPERTIES_TITLE
428
- + DEPRECATED_PROPERTIES_INTRO
429
- + DEPRECATED_BROKER_TITLE
430
- + "".join(deprecated_broker_content)
431
- + DEPRECATED_CLUSTER_TITLE
432
- + "".join(deprecated_cluster_content)
433
- )
434
-
435
- # Write output files.
436
- write_data_to_file(page_folder, OUTPUT_FILE_BROKER, broker_page)
437
- write_data_to_file(page_folder, OUTPUT_FILE_CLUSTER, cluster_page)
438
- write_data_to_file(page_folder, OUTPUT_FILE_CLOUD, cloud_page)
439
- write_data_to_file(page_folder, OUTPUT_FILE_DEPRECATED, deprecated_page)
440
- write_data_to_file(output_dir, ALL_PROPERTIES_FILE, "\n".join(all_properties))
441
-
442
- # Write error files.
443
- write_error_file(
444
- error_folder, ERROR_FILE_DESCRIPTION, "\n".join(empty_description_errors), total_properties
445
- )
446
- write_error_file(
447
- error_folder, ERROR_FILE_TYPE, "\n".join(empty_type_errors), total_properties
448
- )
449
- write_error_file(
450
- error_folder, ERROR_FILE_MAX_WITHOUT_MIN, "\n".join(max_without_min_errors), total_properties
451
- )
452
- write_error_file(
453
- error_folder, ERROR_FILE_MIN_WITHOUT_MAX, "\n".join(min_without_max_errors), total_properties
454
- )
455
- write_error_file(
456
- error_folder, "deprecated_properties.txt", "\n".join(deprecated_properties_errors), total_properties
457
- )
458
-
459
- # Print summary.
460
- print(f"Total properties read: {total_properties}")
461
- print(f"Total Broker properties: {len(broker_config_content)}")
462
- print(f"Total Cluster properties: {len(cluster_config_content)}")
463
- print(f"Total Cloud properties: {len(cloud_config_content)}")
464
-
465
- if __name__ == "__main__":
466
- main()