@redpanda-data/docs-extensions-and-macros 4.8.0 → 4.9.0

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 (30) hide show
  1. package/bin/doc-tools.js +236 -54
  2. package/package.json +1 -1
  3. package/tools/property-extractor/Makefile +68 -50
  4. package/tools/property-extractor/cloud_config.py +594 -0
  5. package/tools/property-extractor/compare-properties.js +378 -0
  6. package/tools/property-extractor/generate-handlebars-docs.js +444 -0
  7. package/tools/property-extractor/helpers/and.js +10 -0
  8. package/tools/property-extractor/helpers/eq.js +9 -0
  9. package/tools/property-extractor/helpers/formatPropertyValue.js +128 -0
  10. package/tools/property-extractor/helpers/formatUnits.js +26 -0
  11. package/tools/property-extractor/helpers/index.js +13 -0
  12. package/tools/property-extractor/helpers/join.js +18 -0
  13. package/tools/property-extractor/helpers/ne.js +9 -0
  14. package/tools/property-extractor/helpers/not.js +8 -0
  15. package/tools/property-extractor/helpers/or.js +10 -0
  16. package/tools/property-extractor/helpers/renderPropertyExample.js +42 -0
  17. package/tools/property-extractor/package-lock.json +77 -0
  18. package/tools/property-extractor/package.json +6 -0
  19. package/tools/property-extractor/parser.py +27 -1
  20. package/tools/property-extractor/property_extractor.py +1428 -49
  21. package/tools/property-extractor/requirements.txt +2 -0
  22. package/tools/property-extractor/templates/deprecated-properties.hbs +25 -0
  23. package/tools/property-extractor/templates/deprecated-property.hbs +7 -0
  24. package/tools/property-extractor/templates/property-cloud.hbs +105 -0
  25. package/tools/property-extractor/templates/property-page.hbs +22 -0
  26. package/tools/property-extractor/templates/property.hbs +85 -0
  27. package/tools/property-extractor/templates/topic-property-cloud.hbs +97 -0
  28. package/tools/property-extractor/templates/topic-property.hbs +73 -0
  29. package/tools/property-extractor/transformers.py +178 -6
  30. package/tools/property-extractor/json-to-asciidoc/generate_docs.py +0 -491
@@ -1,2 +1,4 @@
1
1
  tree_sitter==0.21.1
2
2
  setuptools>=42.0.0
3
+ pyyaml>=6.0
4
+ requests>=2.32.5
@@ -0,0 +1,25 @@
1
+ {{#if deprecated}}
2
+ = Deprecated Configuration Properties
3
+ :description: This is an exhaustive list of all the deprecated properties.
4
+
5
+ This is an exhaustive list of all the deprecated properties.
6
+
7
+ {{#if brokerProperties}}
8
+ == Broker properties
9
+
10
+ {{#each brokerProperties}}
11
+ {{> deprecated-property}}
12
+
13
+ {{/each}}
14
+ {{/if}}
15
+
16
+ {{#if clusterProperties}}
17
+ == Cluster properties
18
+
19
+ {{#each clusterProperties}}
20
+ {{> deprecated-property}}
21
+
22
+ {{/each}}
23
+ {{/if}}
24
+
25
+ {{/if}}
@@ -0,0 +1,7 @@
1
+ === `{{name}}`
2
+
3
+ {{#if description}}
4
+ {{{description}}}
5
+ {{else}}
6
+ No description available.
7
+ {{/if}}
@@ -0,0 +1,105 @@
1
+ {{#if cloud_supported}}
2
+ // tag::redpanda-cloud[]
3
+ {{/if}}
4
+ === {{name}}
5
+ {{#if version}}
6
+
7
+ *Introduced in {{version}}*
8
+ {{/if}}
9
+ {{#if description}}
10
+
11
+ {{{description}}}
12
+ {{else}}
13
+
14
+ No description available.
15
+ {{/if}}
16
+ {{#if is_enterprise}}
17
+
18
+ ifndef::env-cloud[]
19
+ *Enterprise license required*: `{{enterprise_value}}` (for license details, see xref:get-started:licensing/index.adoc[Redpanda Licensing])
20
+ endif::[]
21
+ {{/if}}
22
+ {{#if cloud_byoc_only}}
23
+
24
+ NOTE: This property is available only in Redpanda Cloud BYOC deployments.
25
+ {{/if}}
26
+ {{#if units}}
27
+
28
+ *Unit:* {{units}}
29
+ {{else}}
30
+ {{#if (formatUnits name)}}
31
+
32
+ *Unit:* {{formatUnits name}}
33
+ {{/if}}
34
+ {{/if}}
35
+ {{#if (ne defined_in "src/v/config/node_config.cc")}}
36
+ {{#if (ne needs_restart undefined)}}
37
+
38
+ *Requires restart:* {{#if needs_restart}}Yes{{else}}No{{/if}}
39
+ {{/if}}
40
+ {{/if}}
41
+ {{#if visibility}}
42
+
43
+ // tag::self-managed-only[]
44
+ *Visibility:* `{{visibility}}`
45
+ // end::self-managed-only[]
46
+ {{/if}}
47
+ {{#if type}}
48
+
49
+ *Type:* {{type}}
50
+ {{/if}}
51
+ {{#if (and minimum maximum)}}
52
+
53
+ *Accepted values:* [`{{minimum}}`, `{{maximum}}`]
54
+ {{else}}
55
+ {{#if minimum}}
56
+
57
+ *Minimum value:* `{{minimum}}`
58
+ {{/if}}
59
+ {{#if maximum}}
60
+
61
+ *Maximum value:* `{{maximum}}`
62
+ {{/if}}
63
+ {{/if}}
64
+ {{#if (ne default undefined)}}
65
+
66
+ ifdef::env-cloud[]
67
+ *Default:* Available in the Redpanda Cloud Console
68
+ endif::[]
69
+ ifndef::env-cloud[]
70
+ *Default:* `{{formatPropertyValue default type}}`
71
+ endif::[]
72
+ {{/if}}
73
+
74
+ // tag::self-managed-only[]
75
+ *Nullable:* {{#if nullable}}Yes{{else}}No{{/if}}
76
+ // end::self-managed-only[]
77
+ {{#if example}}
78
+
79
+ {{{renderPropertyExample this}}}
80
+ {{/if}}
81
+ {{#if related_topics}}
82
+
83
+ *Related topics:*
84
+
85
+ {{#each related_topics}}
86
+ * {{{this}}}
87
+ {{/each}}
88
+ {{/if}}
89
+ {{#if aliases}}
90
+
91
+ // tag::self-managed-only[]
92
+ *Aliases:* {{join aliases ", "}}
93
+ // end::self-managed-only[]
94
+ {{/if}}
95
+ {{#if is_deprecated}}
96
+
97
+ [WARNING]
98
+ ====
99
+ This property is deprecated.
100
+ ====
101
+ {{/if}}
102
+ ---
103
+ {{#if cloud_supported}}
104
+ // end::redpanda-cloud[]
105
+ {{/if}}
@@ -0,0 +1,22 @@
1
+ = {{pageTitle}}
2
+ {{#if pageAliases}}
3
+ :page-aliases: {{join pageAliases ", "}}
4
+ {{/if}}
5
+ :description: {{description}}
6
+
7
+ {{{intro}}}
8
+
9
+ {{#if sectionTitle}}
10
+ == {{sectionTitle}}
11
+ {{/if}}
12
+
13
+ {{#if sectionIntro}}
14
+ {{{sectionIntro}}}
15
+ {{/if}}
16
+
17
+ {{#each groups}}
18
+ {{#each this.properties}}
19
+ {{> (lookup ../this "template")}}
20
+
21
+ {{/each}}
22
+ {{/each}}
@@ -0,0 +1,85 @@
1
+ === {{name}}
2
+
3
+ {{#if version}}
4
+ *Introduced in {{version}}*
5
+ {{/if}}
6
+
7
+ {{#if description}}
8
+ {{{description}}}
9
+ {{else}}
10
+ No description available.
11
+ {{/if}}
12
+
13
+ {{#if is_enterprise}}
14
+ ifndef::env-cloud[]
15
+ *Enterprise license required*: `{{enterprise_value}}` (for license details, see xref:get-started:licensing/index.adoc[Redpanda Licensing])
16
+ endif::[]
17
+
18
+ {{/if}}
19
+
20
+ {{#if units}}
21
+ *Unit:* {{units}}
22
+
23
+ {{else}}
24
+ {{#if (formatUnits name)}}
25
+ *Unit:* {{formatUnits name}}
26
+
27
+ {{/if}}
28
+ {{/if}}
29
+ {{#if (ne defined_in "src/v/config/node_config.cc")}}
30
+ {{#if (ne needs_restart undefined)}}
31
+ *Requires restart:* {{#if needs_restart}}Yes{{else}}No{{/if}}
32
+
33
+ {{/if}}
34
+ {{/if}}
35
+ {{#if visibility}}
36
+ *Visibility:* `{{visibility}}`
37
+
38
+ {{/if}}
39
+ {{#if type}}
40
+ *Type:* {{type}}
41
+
42
+ {{/if}}
43
+ {{#if (and minimum maximum)}}
44
+ *Accepted values:* [`{{minimum}}`, `{{maximum}}`]
45
+
46
+ {{else}}
47
+ {{#if minimum}}
48
+ *Minimum value:* `{{minimum}}`
49
+
50
+ {{/if}}
51
+ {{#if maximum}}
52
+ *Maximum value:* `{{maximum}}`
53
+
54
+ {{/if}}
55
+ {{/if}}
56
+ {{#if (ne default undefined)}}
57
+ *Default:* `{{formatPropertyValue default type}}`
58
+
59
+ {{/if}}
60
+ *Nullable:* {{#if nullable}}Yes{{else}}No{{/if}}
61
+
62
+ {{#if example}}
63
+ {{{renderPropertyExample this}}}
64
+ {{/if}}
65
+
66
+ {{#if related_topics}}
67
+ *Related topics:*
68
+
69
+ {{#each related_topics}}
70
+ * {{{this}}}
71
+ {{/each}}
72
+ {{/if}}
73
+
74
+ {{#if aliases}}
75
+ *Aliases:* {{join aliases ", "}}
76
+
77
+ {{/if}}
78
+ {{#if is_deprecated}}
79
+ [WARNING]
80
+ ====
81
+ This property is deprecated.
82
+ ====
83
+
84
+ {{/if}}
85
+ ---
@@ -0,0 +1,97 @@
1
+ {{#if cloud_supported}}
2
+ // tag::redpanda-cloud[]
3
+ {{/if}}
4
+ === {{name}}
5
+ {{#if version}}
6
+
7
+ *Introduced in {{version}}*
8
+ {{/if}}
9
+ {{#if description}}
10
+
11
+ {{{description}}}
12
+ {{else}}
13
+
14
+ No description available.
15
+ {{/if}}
16
+ {{#if is_enterprise}}
17
+
18
+ ifndef::env-cloud[]
19
+ *Enterprise license required*: `{{enterprise_value}}` (for license details, see xref:get-started:licensing/index.adoc[Redpanda Licensing])
20
+ endif::[]
21
+ {{/if}}
22
+ {{#if cloud_byoc_only}}
23
+
24
+ NOTE: This property is only available in Redpanda Cloud BYOC deployments.
25
+ {{/if}}
26
+ {{#if type}}
27
+
28
+ *Type:* {{type}}
29
+ {{/if}}
30
+ {{#if acceptable_values}}
31
+
32
+ *Accepted values:* {{{acceptable_values}}}
33
+ {{/if}}
34
+ {{#if corresponding_cluster_property}}
35
+
36
+ *Related cluster property:* xref:reference:cluster-properties.adoc#{{corresponding_cluster_property}}[{{corresponding_cluster_property}}]
37
+ {{/if}}
38
+ {{#if (and minimum maximum)}}
39
+
40
+ *Accepted values:* [`{{minimum}}`, `{{maximum}}`]
41
+ {{else}}
42
+ {{#if minimum}}
43
+
44
+ *Minimum value:* `{{minimum}}`
45
+ {{/if}}
46
+ {{#if maximum}}
47
+
48
+ *Maximum value:* `{{maximum}}`
49
+ {{/if}}
50
+ {{/if}}
51
+ {{#if (ne default undefined)}}
52
+ {{#if cloud_supported}}
53
+
54
+ ifdef::env-cloud[]
55
+ *Default:* Available in the Redpanda Cloud Console
56
+ endif::[]
57
+ ifndef::env-cloud[]
58
+ *Default:* `{{formatPropertyValue default type}}`
59
+ endif::[]
60
+ {{else}}
61
+
62
+ *Default:* `{{formatPropertyValue default type}}`
63
+ {{/if}}
64
+ {{/if}}
65
+
66
+ // tag::self-managed-only[]
67
+ *Nullable:* {{#if nullable}}Yes{{else}}No{{/if}}
68
+ // end::self-managed-only[]
69
+ {{#if example}}
70
+
71
+ {{{renderPropertyExample this}}}
72
+ {{/if}}
73
+ {{#if related_topics}}
74
+
75
+ *Related topics:*
76
+
77
+ {{#each related_topics}}
78
+ * {{{this}}}
79
+ {{/each}}
80
+ {{/if}}
81
+ {{#if aliases}}
82
+
83
+ // tag::self-managed-only[]
84
+ *Aliases:* {{join aliases ", "}}
85
+ // end::self-managed-only[]
86
+ {{/if}}
87
+ {{#if is_deprecated}}
88
+
89
+ [WARNING]
90
+ ====
91
+ This property is deprecated.
92
+ ====
93
+ {{/if}}
94
+ ---
95
+ {{#if cloud_supported}}
96
+ // end::redpanda-cloud[]
97
+ {{/if}}
@@ -0,0 +1,73 @@
1
+ === {{name}}
2
+ {{#if version}}
3
+
4
+ *Introduced in {{version}}*
5
+ {{/if}}
6
+ {{#if description}}
7
+
8
+ {{{description}}}
9
+ {{else}}
10
+
11
+ No description available.
12
+ {{/if}}
13
+ {{#if is_enterprise}}
14
+
15
+ ifndef::env-cloud[]
16
+ *Enterprise license required*: `{{enterprise_value}}` (for license details, see xref:get-started:licensing/index.adoc[Redpanda Licensing])
17
+ endif::[]
18
+ {{/if}}
19
+ {{#if type}}
20
+
21
+ *Type:* {{type}}
22
+ {{/if}}
23
+ {{#if acceptable_values}}
24
+
25
+ *Accepted values:* {{{acceptable_values}}}
26
+ {{/if}}
27
+ {{#if corresponding_cluster_property}}
28
+
29
+ *Related cluster property:* xref:reference:cluster-properties.adoc#{{corresponding_cluster_property}}[{{corresponding_cluster_property}}]
30
+ {{/if}}
31
+ {{#if (and minimum maximum)}}
32
+
33
+ *Accepted values:* [`{{minimum}}`, `{{maximum}}`]
34
+ {{else}}
35
+ {{#if minimum}}
36
+
37
+ *Minimum value:* `{{minimum}}`
38
+ {{/if}}
39
+ {{#if maximum}}
40
+
41
+ *Maximum value:* `{{maximum}}`
42
+ {{/if}}
43
+ {{/if}}
44
+ {{#if (ne default undefined)}}
45
+
46
+ *Default:* `{{formatPropertyValue default type}}`
47
+ {{/if}}
48
+
49
+ *Nullable:* {{#if nullable}}Yes{{else}}No{{/if}}
50
+ {{#if example}}
51
+
52
+ {{{renderPropertyExample this}}}
53
+ {{/if}}
54
+ {{#if related_topics}}
55
+
56
+ *Related topics:*
57
+
58
+ {{#each related_topics}}
59
+ * {{{this}}}
60
+ {{/each}}
61
+ {{/if}}
62
+ {{#if aliases}}
63
+
64
+ *Aliases:* {{join aliases ", "}}
65
+ {{/if}}
66
+ {{#if is_deprecated}}
67
+
68
+ [WARNING]
69
+ ====
70
+ This property is deprecated.
71
+ ====
72
+ {{/if}}
73
+ ---
@@ -1,13 +1,50 @@
1
1
  import re
2
+ import logging
2
3
  from property_bag import PropertyBag
3
4
  from parser import normalize_string
4
5
 
6
+ # Get logger for this module
7
+ logger = logging.getLogger(__name__)
8
+
9
+ # Import the process_enterprise_value function from property_extractor
10
+ # Note: We import at function level to avoid circular imports since property_extractor
11
+ # imports transformers.py. This pattern allows the EnterpriseTransformer to access
12
+ # the centralized enterprise value processing logic without creating import cycles.
13
+ def get_process_enterprise_value():
14
+ """
15
+ Lazily import and return the centralized `process_enterprise_value` function from `property_extractor`.
16
+
17
+ Attempts to import `process_enterprise_value` and return it to avoid circular-import issues. If the import fails an error message is printed and None is returned.
18
+
19
+ Returns:
20
+ Callable or None: The `process_enterprise_value` callable when available, otherwise `None`.
21
+ """
22
+ try:
23
+ from property_extractor import process_enterprise_value
24
+ return process_enterprise_value
25
+ except ImportError as e:
26
+ logger.error("Cannot import process_enterprise_value from property_extractor: %s", e)
27
+ return None
28
+
5
29
 
6
30
  class BasicInfoTransformer:
7
31
  def accepts(self, info, file_pair):
32
+ """
33
+ Always accepts the provided info and file_pair.
34
+
35
+ Parameters:
36
+ info (dict): Parsed metadata for a property (annotation/params/declaration).
37
+ file_pair (object): Pair of source/implementation file metadata used by transformers.
38
+
39
+ Returns:
40
+ bool: Always returns True, indicating this transformer should be applied.
41
+ """
8
42
  return True
9
43
 
10
44
  def parse(self, property, info, file_pair):
45
+ if not info.get("params") or len(info["params"]) == 0:
46
+ return property
47
+
11
48
  property["name"] = info["params"][0]["value"]
12
49
  property["defined_in"] = re.sub(
13
50
  r"^.*src/", "src/", str(file_pair.implementation)
@@ -36,13 +73,52 @@ class IsNullableTransformer:
36
73
 
37
74
 
38
75
  class IsArrayTransformer:
76
+ """
77
+ Detects properties that should be treated as arrays based on their C++ type declarations.
78
+
79
+ This transformer identifies two types of array properties:
80
+ 1. std::vector<T> - Standard C++ vectors
81
+ 2. one_or_many_property<T> - Redpanda's custom type that accepts either a single value or an array
82
+
83
+ The one_or_many_property type is used in Redpanda configuration for properties like 'admin'
84
+ and 'admin_api_tls' where users can specify either:
85
+ - A single object: admin: {address: "127.0.0.1", port: 9644}
86
+ - An array of objects: admin: [{address: "127.0.0.1", port: 9644}, {address: "0.0.0.0", port: 9645}]
87
+
88
+ When detected, these properties are marked with:
89
+ - type: "array"
90
+ - items: {type: <inner_type>} where <inner_type> is extracted from T
91
+ """
92
+
93
+ # Class-level constants for array type patterns
94
+ ARRAY_PATTERN_STD_VECTOR = "std::vector"
95
+ ARRAY_PATTERN_ONE_OR_MANY = "one_or_many_property"
96
+
39
97
  def __init__(self, type_transformer):
40
98
  self.type_transformer = type_transformer
41
99
 
42
100
  def accepts(self, info, file_pair):
43
- return "std::vector" in info["declaration"]
101
+ """
102
+ Check if this property declaration represents an array type.
103
+
104
+ Returns True for:
105
+ - std::vector<T> declarations (standard C++ vectors)
106
+ - one_or_many_property<T> declarations (Redpanda's flexible array type)
107
+ """
108
+ return (self.ARRAY_PATTERN_STD_VECTOR in info["declaration"] or
109
+ self.ARRAY_PATTERN_ONE_OR_MANY in info["declaration"])
44
110
 
45
111
  def parse(self, property, info, file_pair):
112
+ """
113
+ Transform the property to indicate it's an array type.
114
+
115
+ Sets:
116
+ - property["type"] = "array"
117
+ - property["items"]["type"] = <extracted_inner_type>
118
+
119
+ The inner type is extracted by the type_transformer, which handles
120
+ removing the wrapper (std::vector<> or one_or_many_property<>) to get T.
121
+ """
46
122
  property["type"] = "array"
47
123
  property["items"] = PropertyBag()
48
124
  property["items"]["type"] = self.type_transformer.get_type_from_declaration(
@@ -94,10 +170,35 @@ class VisibilityTransformer:
94
170
 
95
171
 
96
172
  class TypeTransformer:
173
+
174
+ # Class-level constants for type pattern matching
175
+ # Shared with IsArrayTransformer for consistency
176
+ ARRAY_PATTERN_STD_VECTOR = "std::vector"
177
+ ARRAY_PATTERN_ONE_OR_MANY = "one_or_many_property"
178
+ OPTIONAL_PATTERN = "std::optional"
179
+
97
180
  def accepts(self, info, file_pair):
98
181
  return True
99
182
 
100
183
  def get_cpp_type_from_declaration(self, declaration):
184
+ """
185
+ Extract the inner type from C++ property declarations.
186
+
187
+ This method handles various C++ template types and extracts the core type T from:
188
+ - property<T> -> T
189
+ - std::optional<T> -> T
190
+ - std::vector<T> -> T
191
+ - one_or_many_property<T> -> T (Redpanda's flexible array type)
192
+
193
+ For one_or_many_property, this is crucial because it allows the same property
194
+ to accept either a single value or an array of values in the configuration.
195
+ Examples:
196
+ - one_or_many_property<model::broker_endpoint> -> model::broker_endpoint
197
+ - one_or_many_property<endpoint_tls_config> -> endpoint_tls_config
198
+
199
+ The extracted type is then used to determine the JSON schema type and
200
+ for resolving default values from the definitions.
201
+ """
101
202
  one_line_declaration = declaration.replace("\n", "").strip()
102
203
  raw_type = (
103
204
  re.sub(r"^.*property<(.+)>.*", "\\1", one_line_declaration)
@@ -105,11 +206,19 @@ class TypeTransformer:
105
206
  .replace(",", "")
106
207
  )
107
208
 
108
- if "std::optional" in raw_type:
209
+ if self.OPTIONAL_PATTERN in raw_type:
109
210
  raw_type = re.sub(".*std::optional<(.+)>.*", "\\1", raw_type)
110
211
 
111
- if "std::vector" in raw_type:
212
+ if self.ARRAY_PATTERN_STD_VECTOR in raw_type:
112
213
  raw_type = re.sub(".*std::vector<(.+)>.*", "\\1", raw_type)
214
+
215
+ # Handle one_or_many_property<T> - extract the inner type T
216
+ # This is essential for Redpanda's flexible configuration properties
217
+ # that can accept either single values or arrays
218
+ # Check and extract from raw_type for consistency with other type extractors
219
+ if self.ARRAY_PATTERN_ONE_OR_MANY in raw_type:
220
+ raw_type = re.sub(".*one_or_many_property<(.+)>.*", "\\1", raw_type)
221
+ raw_type = raw_type.split()[0].replace(",", "")
113
222
 
114
223
  return raw_type
115
224
 
@@ -282,6 +391,10 @@ class FriendlyDefaultTransformer:
282
391
  - std::chrono::milliseconds(10)
283
392
  - std::nullopt
284
393
  """
394
+
395
+ # Class-level constants for pattern matching in default values
396
+ ARRAY_PATTERN_STD_VECTOR = "std::vector"
397
+
285
398
  def accepts(self, info, file_pair):
286
399
  return info.get("params") and len(info["params"]) > 3
287
400
 
@@ -308,7 +421,7 @@ class FriendlyDefaultTransformer:
308
421
  return property
309
422
 
310
423
  # Transform std::vector defaults.
311
- if "std::vector" in default:
424
+ if self.ARRAY_PATTERN_STD_VECTOR in default:
312
425
  m = re.search(r'\{([^}]+)\}', default)
313
426
  if m:
314
427
  contents = m.group(1).strip()
@@ -351,13 +464,65 @@ class AliasTransformer:
351
464
 
352
465
 
353
466
  class EnterpriseTransformer:
467
+ """
468
+ Transforms enterprise property values from C++ expressions to user-friendly JSON.
469
+
470
+ This transformer processes enterprise values by delegating to the
471
+ centralized process_enterprise_value function which handles the full range of
472
+ C++ expression types found in enterprise property definitions.
473
+ """
354
474
  def accepts(self, info, file_pair):
475
+ """
476
+ Return True if the provided info indicates an enterprise-only property.
477
+
478
+ Parameters:
479
+ info (dict): The metadata dictionary for a property. This function checks for a 'type' key whose string contains 'enterprise'.
480
+ file_pair: Unused; present for transformer interface compatibility.
481
+
482
+ Returns:
483
+ bool: True when info contains a 'type' that includes 'enterprise', otherwise False.
484
+ """
355
485
  return bool(info.get('type') and 'enterprise' in info['type'])
356
486
 
357
487
  def parse(self, property, info, file_pair):
488
+ """
489
+ Mark a property as enterprise-only and attach its enterprise value.
490
+
491
+ If an enterprise value is present in info['params'][0]['value'], this method attempts to process it using the shared
492
+ process_enterprise_value helper (loaded via get_process_enterprise_value()). If the processor is unavailable or raises
493
+ an exception, the raw enterprise value is used.
494
+
495
+ Side effects:
496
+ - Sets property["enterprise_value"] to the processed or raw value.
497
+ - Sets property["is_enterprise"] = True.
498
+ - Removes the first element from info['params'].
499
+
500
+ Parameters:
501
+ property (dict): Property bag to modify and return.
502
+ info (dict): Parsed metadata; must have a non-None 'params' list for processing.
503
+ file_pair: Unused here but accepted for transformer API compatibility.
504
+
505
+ Returns:
506
+ dict: The updated property bag.
507
+ """
358
508
  if info['params'] is not None:
359
509
  enterpriseValue = info['params'][0]['value']
360
- property['enterprise_value'] = enterpriseValue
510
+
511
+ # Get the processing function
512
+ process_enterprise_value = get_process_enterprise_value()
513
+ if process_enterprise_value is None:
514
+ property["enterprise_value"] = enterpriseValue
515
+ property['is_enterprise'] = True
516
+ del info['params'][0]
517
+ return property
518
+
519
+ try:
520
+ processed_value = process_enterprise_value(enterpriseValue)
521
+ property["enterprise_value"] = processed_value
522
+ except Exception:
523
+ # Fallback to raw value if processing fails
524
+ property["enterprise_value"] = enterpriseValue
525
+
361
526
  property['is_enterprise'] = True
362
527
  del info['params'][0]
363
528
  return property
@@ -386,7 +551,14 @@ class MetaParamTransformer:
386
551
  iterable_params = info['params']
387
552
  for param in iterable_params:
388
553
  if isinstance(param['value'], str) and param['value'].startswith("meta{"):
389
- meta_content = param['value'].strip("meta{ }").strip()
554
+ # Extract content between meta{ and } using explicit slicing
555
+ param_value = param['value']
556
+ if param_value.endswith('}'):
557
+ meta_content = param_value[5:-1].strip() # Remove "meta{" and "}"
558
+ else:
559
+ # Handle malformed meta{ without closing }
560
+ meta_content = param_value[5:].strip() # Remove "meta{" only
561
+
390
562
  meta_dict = {}
391
563
  for item in meta_content.split(','):
392
564
  item = item.strip()