diff --git a/.changes/unreleased/Under the Hood-20241023-103140.yaml b/.changes/unreleased/Under the Hood-20241023-103140.yaml new file mode 100644 index 00000000000..dc32458378b --- /dev/null +++ b/.changes/unreleased/Under the Hood-20241023-103140.yaml @@ -0,0 +1,6 @@ +kind: Under the Hood +body: Behavior change for cumulative metric type param +time: 2024-10-23T10:31:40.698164-05:00 +custom: + Author: DevonFulcher + Issue: None diff --git a/core/dbt/contracts/graph/semantic_manifest.py b/core/dbt/contracts/graph/semantic_manifest.py index 82db87c5a16..fbd6da69198 100644 --- a/core/dbt/contracts/graph/semantic_manifest.py +++ b/core/dbt/contracts/graph/semantic_manifest.py @@ -1,4 +1,4 @@ -from typing import List, Optional +from typing import List, Optional, Set from dbt import deprecations from dbt.constants import ( @@ -58,8 +58,19 @@ def validate(self) -> bool: return True semantic_manifest = self._get_pydantic_semantic_manifest() - validator = SemanticManifestValidator[PydanticSemanticManifest]() - validation_results = validator.validate_semantic_manifest(semantic_manifest) + + metrics_using_old_params: Set[str] = set() + for metric in semantic_manifest.metrics or []: + for field in ("window", "grain_to_date"): + type_params_field_value = getattr(metric.type_params, field) + # Warn that the old type_params structure has been deprecated. + if type_params_field_value: + metrics_using_old_params.add(metric.name) + if metrics_using_old_params: + deprecations.warn( + "mf-cumulative-type-params-deprecation", + ) + new_time_spines = semantic_manifest.project_configuration.time_spines old_time_spines = semantic_manifest.project_configuration.time_spine_table_configurations # If the new time spine contains a day grain then it is functionally equivalent to the legacy time spine. @@ -75,6 +86,9 @@ def validate(self) -> bool: "mf-timespine-without-yaml-configuration", ) + validator = SemanticManifestValidator[PydanticSemanticManifest]() + validation_results = validator.validate_semantic_manifest(semantic_manifest) + for warning in validation_results.warnings: fire_event(SemanticValidationFailure(msg=warning.message)) diff --git a/core/dbt/contracts/project.py b/core/dbt/contracts/project.py index a71dbd3ff27..aea26bbca84 100644 --- a/core/dbt/contracts/project.py +++ b/core/dbt/contracts/project.py @@ -345,6 +345,7 @@ class ProjectFlags(ExtensibleDbtClassMixin): state_modified_compare_more_unrendered_values: bool = False state_modified_compare_vars: bool = False allow_mf_time_spines_without_yaml_configuration: bool = False + allow_legacy_mf_cumulative_type_params: bool = False @property def project_only_flags(self) -> Dict[str, Any]: @@ -356,6 +357,7 @@ def project_only_flags(self) -> Dict[str, Any]: "state_modified_compare_more_unrendered_values": self.state_modified_compare_more_unrendered_values, "state_modified_compare_vars": self.state_modified_compare_vars, "allow_mf_time_spines_without_yaml_configuration": self.allow_mf_time_spines_without_yaml_configuration, + "allow_legacy_mf_cumulative_type_params": self.allow_legacy_mf_cumulative_type_params, } diff --git a/core/dbt/deprecations.py b/core/dbt/deprecations.py index 147212e43a3..e4752c6c857 100644 --- a/core/dbt/deprecations.py +++ b/core/dbt/deprecations.py @@ -123,6 +123,11 @@ class MFTimespineWithoutYamlConfigurationDeprecation(DBTDeprecation): _event = "MFTimespineWithoutYamlConfigurationDeprecation" +class MFCumulativeTypeParamsDeprecation(DBTDeprecation): + _name = "mf-cumulative-type-params-deprecation" + _event = "MFCumulativeTypeParamsDeprecation" + + def renamed_env_var(old_name: str, new_name: str): class EnvironmentVariableRenamed(DBTDeprecation): _name = f"environment-variable-renamed:{old_name}" @@ -172,6 +177,7 @@ def show_callback(): ResourceNamesWithSpacesDeprecation(), SourceFreshnessProjectHooksNotRun(), MFTimespineWithoutYamlConfigurationDeprecation(), + MFCumulativeTypeParamsDeprecation(), ] deprecations: Dict[str, DBTDeprecation] = {d.name: d for d in deprecations_list} diff --git a/core/dbt/events/core_types.proto b/core/dbt/events/core_types.proto index fba512f1502..aa860e4d32c 100644 --- a/core/dbt/events/core_types.proto +++ b/core/dbt/events/core_types.proto @@ -453,6 +453,14 @@ message MFTimespineWithoutYamlConfigurationDeprecationMsg { MFTimespineWithoutYamlConfigurationDeprecation data = 2; } +// D019 +message MFCumulativeTypeParamsDeprecation {} + +message MFCumulativeTypeParamsDeprecationMsg { + CoreEventInfo info = 1; + MFCumulativeTypeParamsDeprecation data = 2; +} + // I065 message DeprecatedModel { string model_name = 1; diff --git a/core/dbt/events/types.py b/core/dbt/events/types.py index 0feab122716..921fcf908ec 100644 --- a/core/dbt/events/types.py +++ b/core/dbt/events/types.py @@ -476,6 +476,16 @@ def message(self) -> str: return line_wrap_message(warning_tag(description)) +class MFCumulativeTypeParamsDeprecation(WarnLevel): + def code(self) -> str: + return "D019" + + def message(self) -> str: + description = "Cumulative fields `type_params.window` and `type_params.grain_to_date` have been moved and will soon be deprecated. Please nest those values under `type_params.cumulative_type_params.window` and `type_params.cumulative_type_params.grain_to_date`. See documentation on behavior changes: https://docs.getdbt.com/reference/global-configs/behavior-changes." + + return line_wrap_message(warning_tag(description)) + + # ======================================================= # I - Project parsing # ======================================================= diff --git a/tests/unit/contracts/graph/test_semantic_manifest.py b/tests/unit/contracts/graph/test_semantic_manifest.py index ec6b4494f70..608021acd15 100644 --- a/tests/unit/contracts/graph/test_semantic_manifest.py +++ b/tests/unit/contracts/graph/test_semantic_manifest.py @@ -3,8 +3,12 @@ import pytest from core.dbt.contracts.graph.manifest import Manifest -from core.dbt.contracts.graph.nodes import ModelNode +from core.dbt.contracts.graph.nodes import Metric, ModelNode +from dbt.artifacts.resources.types import NodeType +from dbt.artifacts.resources.v1.metric import MetricTimeWindow, MetricTypeParams from dbt.contracts.graph.semantic_manifest import SemanticManifest +from dbt_semantic_interfaces.type_enums import TimeGranularity +from dbt_semantic_interfaces.type_enums.metric_type import MetricType # Overwrite the default nods to construct the manifest @@ -46,3 +50,50 @@ def test_allow_mf_time_spines_without_yaml_configuration( sm_manifest = SemanticManifest(manifest) assert sm_manifest.validate() assert patched_deprecations.warn.call_count == 1 + + @pytest.mark.parametrize( + "metric", + [ + ( + Metric( + name="my_metric", + type=MetricType.CUMULATIVE, + type_params=MetricTypeParams(grain_to_date=TimeGranularity.MONTH), + resource_type=NodeType.Metric, + package_name="test", + path="models/test/my_metric.yml", + original_file_path="models/test/my_metric.yml", + unique_id="metric.test.my_metric", + fqn=["test", "my_metric"], + description="My metric", + label="My Metric", + ) + ), + ( + Metric( + name="my_metric", + type=MetricType.CUMULATIVE, + type_params=MetricTypeParams( + window=MetricTimeWindow(count=1, granularity=TimeGranularity.MONTH) + ), + resource_type=NodeType.Metric, + package_name="test", + path="models/test/my_metric.yml", + original_file_path="models/test/my_metric.yml", + unique_id="metric.test.my_metric", + fqn=["test", "my_metric"], + description="My metric", + label="My Metric", + ) + ), + ], + ) + def test_deprecate_cumulative_type_params(self, manifest: Manifest, metric: Metric): + with patch("dbt.contracts.graph.semantic_manifest.get_flags") as patched_get_flags, patch( + "dbt.contracts.graph.semantic_manifest.deprecations" + ) as patched_deprecations: + patched_get_flags.return_value.allow_legacy_mf_cumulative_type_params = False + manifest.metrics[metric.unique_id] = metric + sm_manifest = SemanticManifest(manifest) + assert sm_manifest.validate() + assert patched_deprecations.warn.call_count == 1 diff --git a/tests/unit/test_events.py b/tests/unit/test_events.py index 0c6584879e0..3f3ffe903cb 100644 --- a/tests/unit/test_events.py +++ b/tests/unit/test_events.py @@ -158,6 +158,7 @@ def test_event_codes(self): ), core_types.SourceFreshnessProjectHooksNotRun(), core_types.MFTimespineWithoutYamlConfigurationDeprecation(), + core_types.MFCumulativeTypeParamsDeprecation(), # E - DB Adapter ====================== adapter_types.AdapterEventDebug(), adapter_types.AdapterEventInfo(),