|
5 | 5 | from dbt.adapters.contracts.relation import RelationConfig |
6 | 6 | from dbt.adapters.relation_configs.config_base import RelationResults |
7 | 7 | from dbt_common.exceptions import DbtRuntimeError |
8 | | -from pydantic import model_validator |
| 8 | +from pydantic import root_validator |
9 | 9 |
|
10 | 10 | from dbt.adapters.databricks.relation_configs import base |
11 | 11 | from dbt.adapters.databricks.relation_configs.base import ( |
@@ -41,6 +41,9 @@ class RefreshMode(str, Enum): |
41 | 41 | # these all canonicalize to the same UTC for equality. |
42 | 42 | _UTC_ALIASES = {"UTC", "ETC/UTC"} |
43 | 43 |
|
| 44 | +# Mutually-exclusive mode discriminators on RefreshConfig. |
| 45 | +_MODE_FIELDS = ("cron", "every", "on_update") |
| 46 | + |
44 | 47 |
|
45 | 48 | def _canonical_tz(tz: Optional[str]) -> str: |
46 | 49 | s = (tz or "UTC").upper() |
@@ -98,33 +101,27 @@ class RefreshConfig(DatabricksComponentConfig): |
98 | 101 | # affect identity. |
99 | 102 | is_altered: bool = False |
100 | 103 |
|
101 | | - @model_validator(mode="after") |
102 | | - def _validate_mode_fields(self) -> "RefreshConfig": |
103 | | - modes_set = [name for name, value in self._mode_signals() if value] |
| 104 | + @root_validator(skip_on_failure=True) |
| 105 | + def _validate_mode_fields(cls, values: dict) -> dict: |
| 106 | + modes_set = [name for name in _MODE_FIELDS if values.get(name)] |
104 | 107 | if len(modes_set) > 1: |
105 | 108 | raise DbtRuntimeError( |
106 | 109 | f"Refresh schedule must specify at most one of cron / every / on_update;" |
107 | 110 | f" got {modes_set}." |
108 | 111 | ) |
109 | | - if self.time_zone_value is not None and self.cron is None: |
| 112 | + if values.get("time_zone_value") is not None and values.get("cron") is None: |
110 | 113 | raise DbtRuntimeError("`time_zone_value` is only valid when `cron` is set.") |
111 | | - if self.at_most_every is not None: |
112 | | - if not self.on_update: |
| 114 | + at_most_every = values.get("at_most_every") |
| 115 | + if at_most_every is not None: |
| 116 | + if not values.get("on_update"): |
113 | 117 | raise DbtRuntimeError("`at_most_every` is only valid when `on_update` is True.") |
114 | | - seconds = _interval_seconds(self.at_most_every) |
| 118 | + seconds = _interval_seconds(at_most_every) |
115 | 119 | if seconds < 60: |
116 | 120 | raise DbtRuntimeError( |
117 | 121 | f"`at_most_every` must be at least 60 seconds (1 minute);" |
118 | | - f" got {self.at_most_every!r} ({seconds}s)." |
| 122 | + f" got {at_most_every!r} ({seconds}s)." |
119 | 123 | ) |
120 | | - return self |
121 | | - |
122 | | - def _mode_signals(self) -> tuple[tuple[str, Any], ...]: |
123 | | - return ( |
124 | | - ("cron", self.cron), |
125 | | - ("every", self.every), |
126 | | - ("on_update", self.on_update), |
127 | | - ) |
| 124 | + return values |
128 | 125 |
|
129 | 126 | @property |
130 | 127 | def mode(self) -> RefreshMode: |
@@ -176,8 +173,8 @@ def get_diff(self, other: "RefreshConfig") -> Optional["RefreshConfig"]: |
176 | 173 | if self == other: |
177 | 174 | return None |
178 | 175 | is_altered = self.mode != RefreshMode.MANUAL and other.mode != RefreshMode.MANUAL |
179 | | - # model_construct skips re-validation; only is_altered changes, other fields stay valid. |
180 | | - return self.model_construct(**{**self.model_dump(), "is_altered": is_altered}) |
| 176 | + # copy() skips re-validation; only is_altered changes, other fields stay valid. |
| 177 | + return self.copy(update={"is_altered": is_altered}) |
181 | 178 |
|
182 | 179 |
|
183 | 180 | class RefreshProcessor(DatabricksComponentProcessor[RefreshConfig]): |
|
0 commit comments