|
28 | 28 | from .utils import combine_dicts |
29 | 29 |
|
30 | 30 |
|
| 31 | +def delete_nested_key_from_dict(d: dict[str, Any], compound_key: str) -> None: |
| 32 | + """Delete a nested key from a dictionary.""" |
| 33 | + keys = compound_key.split(".") |
| 34 | + for key in keys[:-1]: |
| 35 | + if key in d and isinstance(d[key], dict): |
| 36 | + d = d[key] # type: ignore[reportUnknownVariableType] |
| 37 | + else: |
| 38 | + return |
| 39 | + d.pop(keys[-1], None) |
| 40 | + |
| 41 | + |
| 42 | +def flat_schema_to_index_mapping(flat_schema: dict[str, str]) -> dict[str, Any]: |
| 43 | + """ |
| 44 | + Convert dicts with flat JSON paths and values into a nested mapping with |
| 45 | + intermediary `properties`, `fields` and `type` fields. |
| 46 | + """ |
| 47 | + |
| 48 | + # Sorting here ensures that 'a.b' processed before 'a.b.c', allowing us to correctly |
| 49 | + # detect and handle multi-fields. |
| 50 | + sorted_items = sorted(flat_schema.items()) |
| 51 | + result = {} |
| 52 | + |
| 53 | + for field_path, field_type in sorted_items: |
| 54 | + parts = field_path.split(".") |
| 55 | + current_level = result |
| 56 | + |
| 57 | + for part in parts[:-1]: |
| 58 | + node = current_level.setdefault(part, {}) # type: ignore[reportUnknownVariableType] |
| 59 | + |
| 60 | + if "type" in node and node["type"] not in ("nested", "object"): |
| 61 | + current_level = node.setdefault("fields", {}) # type: ignore[reportUnknownVariableType] |
| 62 | + else: |
| 63 | + current_level = node.setdefault("properties", {}) # type: ignore[reportUnknownVariableType] |
| 64 | + |
| 65 | + leaf_key = parts[-1] |
| 66 | + current_level[leaf_key] = {"type": field_type} |
| 67 | + |
| 68 | + # add `scaling_factor` field missing in the schema |
| 69 | + # https://www.elastic.co/docs/reference/elasticsearch/mapping-reference/number#scaled-float-params |
| 70 | + if field_type == "scaled_float": |
| 71 | + current_level[leaf_key]["scaling_factor"] = 1000 |
| 72 | + |
| 73 | + # add `path` field for `alias` fields, set to a dummy value |
| 74 | + if field_type == "alias": |
| 75 | + current_level[leaf_key]["path"] = "@timestamp" |
| 76 | + |
| 77 | + return result # type: ignore[reportUnknownVariableType] |
| 78 | + |
| 79 | + |
31 | 80 | def get_rule_integrations(metadata: RuleMeta) -> list[str]: |
32 | 81 | """Retrieve rule integrations from metadata.""" |
33 | 82 | if metadata.integration: |
@@ -139,23 +188,23 @@ def prepare_integration_mappings( # noqa: PLR0913 |
139 | 188 |
|
140 | 189 | for stream in package_schema: |
141 | 190 | flat_schema = package_schema[stream] |
142 | | - stream_mappings = utils.flat_schema_to_index_mapping(flat_schema) |
| 191 | + stream_mappings = flat_schema_to_index_mapping(flat_schema) |
143 | 192 | nested_multifields = find_nested_multifields(stream_mappings) |
144 | 193 | for field in nested_multifields: |
145 | 194 | field_name = str(field).split(".fields.")[0].replace(".", ".properties.") + ".fields" |
146 | 195 | log( |
147 | 196 | f"Warning: Nested multi-field `{field}` found in `{integration}-{stream}`. " |
148 | 197 | f"Removing parent field from schema for ES|QL validation." |
149 | 198 | ) |
150 | | - utils.delete_nested_key_from_dict(stream_mappings, field_name) |
| 199 | + delete_nested_key_from_dict(stream_mappings, field_name) |
151 | 200 | nested_flattened_fields = find_flattened_fields_with_subfields(stream_mappings) |
152 | 201 | for field in nested_flattened_fields: |
153 | 202 | field_name = str(field).split(".fields.")[0].replace(".", ".properties.") + ".fields" |
154 | 203 | log( |
155 | 204 | f"Warning: flattened field `{field}` found in `{integration}-{stream}` with sub fields. " |
156 | 205 | f"Removing parent field from schema for ES|QL validation." |
157 | 206 | ) |
158 | | - utils.delete_nested_key_from_dict(stream_mappings, field_name) |
| 207 | + delete_nested_key_from_dict(stream_mappings, field_name) |
159 | 208 | utils.combine_dicts(integration_mappings, stream_mappings) |
160 | 209 | index_lookup[f"{integration}-{stream}"] = stream_mappings |
161 | 210 |
|
|
0 commit comments