@@ -610,12 +610,37 @@ class UnparsedMetricTypeParams(dbtClassMixin):
610610 cumulative_type_params : Optional [UnparsedCumulativeTypeParams ] = None
611611
612612
613+ class UnparsedMetricBase (dbtClassMixin ):
614+ @classmethod
615+ def validate (cls , data ):
616+ super ().validate (data )
617+ if "name" in data :
618+ errors = []
619+ if " " in data ["name" ]:
620+ errors .append ("cannot contain spaces" )
621+ # This handles failing queries due to too long metric names.
622+ # It only occurs in BigQuery and Snowflake (Postgres/Redshift truncate)
623+ if len (data ["name" ]) > 250 :
624+ errors .append ("cannot contain more than 250 characters" )
625+ if not (re .match (r"^[A-Za-z]" , data ["name" ])):
626+ errors .append ("must begin with a letter" )
627+ if not (re .match (r"[\w-]+$" , data ["name" ])):
628+ errors .append ("must contain only letters, numbers and underscores" )
629+
630+ if errors :
631+ raise ParsingError (
632+ f"The metric name '{ data ['name' ]} ' is invalid. It { ', ' .join (e for e in errors )} "
633+ )
634+
635+
613636@dataclass
614- class UnparsedMetric (dbtClassMixin ):
637+ class UnparsedMetric (UnparsedMetricBase ):
638+ """Old-style YAML metric; prefer UnparsedMetricV2 instead as of late 2025."""
639+
615640 name : str
616641 label : str
617642 type : str
618- type_params : UnparsedMetricTypeParams
643+ type_params : UnparsedMetricTypeParams # old-style YAML
619644 description : str = ""
620645 # Note: `Union` must be the outermost part of the type annotation for serialization to work properly.
621646 filter : Union [str , List [str ], None ] = None
@@ -625,22 +650,71 @@ class UnparsedMetric(dbtClassMixin):
625650 tags : List [str ] = field (default_factory = list )
626651 config : Dict [str , Any ] = field (default_factory = dict )
627652
653+
654+ @dataclass
655+ class UnparsedNonAdditiveDimensionV2 (dbtClassMixin ):
656+ name : str
657+ window_agg : str # AggregationType enum
658+ group_by : List [str ] = field (default_factory = list )
659+
660+
661+ @dataclass
662+ class UnparsedMetricV2 (UnparsedMetricBase ):
663+ name : str
664+ label : Optional [str ] = None
665+ hidden : bool = False
666+ description : Optional [str ] = None
667+ type : Optional [str ] = "simple"
668+ agg : Optional [str ] = None
669+
670+ percentile : Optional [float ] = None
671+ percentile_type : Optional [str ] = None
672+
673+ join_to_timespine : Optional [bool ] = None
674+ fill_nulls_with : Optional [int ] = None
675+ expr : Optional [Union [str , int ]] = None
676+ filter : Union [str , List [str ], None ] = None
677+
678+ tags : List [str ] = field (default_factory = list )
679+ meta : Dict [str , Any ] = field (default_factory = dict )
680+ config : Dict [str , Any ] = field (default_factory = dict )
681+
682+ non_additive_dimension : Optional [UnparsedNonAdditiveDimensionV2 ] = None
683+ agg_time_dimension : Optional [str ] = None
684+
685+ # For cumulative metrics
686+ window : Optional [str ] = None
687+ grain_to_date : Optional [str ] = None
688+ period_agg : Optional [str ] = None
689+ input_metric : Optional [Union [str , Dict [str , Any ]]] = None
690+
691+ # For ratio metrics
692+ numerator : Optional [Union [str , Dict [str , Any ]]] = None
693+ denominator : Optional [Union [str , Dict [str , Any ]]] = None
694+
695+ # For derived metrics
696+ input_metrics : Optional [List [Dict [str , Any ]]] = None
697+
698+ # For conversion metrics
699+ entity : Optional [str ] = None
700+ calculation : Optional [str ] = None
701+ base_metric : Optional [Union [str , Dict [str , Any ]]] = None
702+ conversion_metric : Optional [Union [str , Dict [str , Any ]]] = None
703+ constant_properties : Optional [List [Dict [str , Any ]]] = None
704+
628705 @classmethod
629706 def validate (cls , data ):
630- super (UnparsedMetric , cls ).validate (data )
707+ super (UnparsedMetricV2 , cls ).validate (data )
631708 if "name" in data :
632709 errors = []
633710 if " " in data ["name" ]:
634711 errors .append ("cannot contain spaces" )
635- # This handles failing queries due to too long metric names.
636- # It only occurs in BigQuery and Snowflake (Postgres/Redshift truncate)
637712 if len (data ["name" ]) > 250 :
638713 errors .append ("cannot contain more than 250 characters" )
639714 if not (re .match (r"^[A-Za-z]" , data ["name" ])):
640715 errors .append ("must begin with a letter" )
641716 if not (re .match (r"[\w-]+$" , data ["name" ])):
642717 errors .append ("must contain only letters, numbers and underscores" )
643-
644718 if errors :
645719 raise ParsingError (
646720 f"The metric name '{ data ['name' ]} ' is invalid. It { ', ' .join (e for e in errors )} "
0 commit comments