diff --git a/CHANGELOG.md b/CHANGELOG.md index 81b03f08..c1dfda3c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ## Unreleased +## 20.2.0 +- Allow renaming of fields when serializing metrics ([mozilla/glean-dictionary#2309](https://github.com/mozilla/glean-dictionary/issues/2309)) + ## 20.1.0 - Allow to overwrite date to check expiry against using the `SOURCE_DATE_EPOCH` environment variable ([bug 2052175](https://bugzilla.mozilla.org/show_bug.cgi?id=2052175)) diff --git a/glean_parser/metrics.py b/glean_parser/metrics.py index 264495c6..2a945613 100644 --- a/glean_parser/metrics.py +++ b/glean_parser/metrics.py @@ -165,7 +165,9 @@ def make_metric( **metric_info, ) - def serialize(self) -> Dict[str, util.JSONType]: + def serialize( + self, rename_fields: Optional[Dict[str, str]] = None + ) -> Dict[str, util.JSONType]: """ Serialize the metric back to JSON object model. """ @@ -178,6 +180,13 @@ def serialize(self) -> Dict[str, util.JSONType]: d[key] = sorted(list(val)) if isinstance(val, list) and len(val) and isinstance(val[0], enum.Enum): d[key] = [x.name for x in val] + + if rename_fields is not None: + for key, val in rename_fields.items(): + if key in d: + d[val] = d[key] + del d[key] + del d["name"] del d["category"] if not d["unit"]: @@ -399,11 +408,13 @@ def __init__(self, *args, **kwargs): self.labels = None super().__init__(*args, **kwargs) - def serialize(self) -> Dict[str, util.JSONType]: + def serialize( + self, rename_fields: Optional[Dict[str, str]] = None + ) -> Dict[str, util.JSONType]: """ Serialize the metric back to JSON object model. """ - d = super().serialize() + d = super().serialize(rename_fields=rename_fields) d["labels"] = self.ordered_labels del d["ordered_labels"] return d @@ -604,11 +615,13 @@ def __init__(self, *args, **kwargs): self.categories = None super().__init__(*args, **kwargs) - def serialize(self) -> Dict[str, util.JSONType]: + def serialize( + self, rename_fields: Optional[Dict[str, str]] = None + ) -> Dict[str, util.JSONType]: """ Serialize the metric back to JSON object model. """ - d = super().serialize() + d = super().serialize(rename_fields=rename_fields) d["keys"] = self.ordered_keys d["categories"] = self.ordered_categories del d["ordered_keys"] diff --git a/tests/test_parser.py b/tests/test_parser.py index f4a90b61..4a7f3bfc 100644 --- a/tests/test_parser.py +++ b/tests/test_parser.py @@ -1136,6 +1136,88 @@ def test_no_lint_sorted(): assert all_objects.value["tags"]["tag"].no_lint == ["lint1", "lint2"] +def test_allow_renaming_of_fields(): + results = parser.parse_objects( + [ + util.add_required( + { + "category": { + "metric": { + "type": "object", + "structure": { + "type": "array", + "items": { + "type": "object", + "properties": { + "prop_a": { + "type": "string", + }, + "prop_b": { + "type": "boolean", + }, + }, + }, + }, + }, + }, + } + ), + ] + ) + errs = list(results) + assert len(errs) == 0 + + metrics = { + metric.identifier(): metric.serialize( + rename_fields={"_generate_structure": "structure"} + ) + for category, probes in results.value.items() + for probe_name, metric in probes.items() + } + + expected = { + "category.metric": { + "bugs": ["http://bugzilla.mozilla.org/12345678"], + "data_reviews": ["https://example.com/review/"], + "defined_in": {"line": 3}, + "description": "DESCRIPTION...", + "disabled": False, + "expires": "never", + "gecko_datapoint": "", + "in_session": False, + "lifetime": "ping", + "metadata": {}, + "no_lint": [], + "notification_emails": ["nobody@example.com"], + "send_in_pings": ["metrics"], + "type": "object", + "version": 0, + "structure": { + "type": "array", + "items": { + "type": "object", + "properties": { + "prop_a": { + "type": "string", + }, + "prop_b": { + "type": "boolean", + }, + }, + }, + }, + }, + } + expected_json = json.dumps(expected, sort_keys=True, indent=2) + + out_json = json.dumps( + metrics, + sort_keys=True, + indent=2, + ) + assert expected_json == out_json + + def test_no_internal_fields_exposed(): """ We accidentally exposed fields like `_config` and `_generate_enums` before.