Skip to content

Commit 31a9b5e

Browse files
authored
Feat: Add additional fields to environment summary (#4267)
1 parent 99b1512 commit 31a9b5e

File tree

7 files changed

+77
-38
lines changed

7 files changed

+77
-38
lines changed

sqlmesh/core/console.py

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from rich.tree import Tree
2929
from sqlglot import exp
3030

31-
from sqlmesh.core.environment import EnvironmentNamingInfo
31+
from sqlmesh.core.environment import EnvironmentNamingInfo, EnvironmentSummary
3232
from sqlmesh.core.linter.rule import RuleViolation
3333
from sqlmesh.core.model import Model
3434
from sqlmesh.core.snapshot import (
@@ -188,7 +188,7 @@ class EnvironmentsConsole(abc.ABC):
188188
"""Console for displaying environments"""
189189

190190
@abc.abstractmethod
191-
def print_environments(self, environments_summary: t.Dict[str, int]) -> None:
191+
def print_environments(self, environments_summary: t.List[EnvironmentSummary]) -> None:
192192
"""Prints all environment names along with expiry datetime."""
193193

194194
@abc.abstractmethod
@@ -659,7 +659,7 @@ def show_row_diff(
659659
) -> None:
660660
pass
661661

662-
def print_environments(self, environments_summary: t.Dict[str, int]) -> None:
662+
def print_environments(self, environments_summary: t.List[EnvironmentSummary]) -> None:
663663
pass
664664

665665
def show_intervals(self, snapshot_intervals: t.Dict[Snapshot, SnapshotIntervals]) -> None:
@@ -2089,11 +2089,13 @@ def show_row_diff(
20892089
self.console.print(f"\n[b][green]{target_name} ONLY[/green] sample rows:[/b]")
20902090
self.console.print(row_diff.t_sample.to_string(index=False), end="\n\n")
20912091

2092-
def print_environments(self, environments_summary: t.Dict[str, int]) -> None:
2092+
def print_environments(self, environments_summary: t.List[EnvironmentSummary]) -> None:
20932093
"""Prints all environment names along with expiry datetime."""
20942094
output = [
2095-
f"{name} - {time_like_to_str(ts)}" if ts else f"{name} - No Expiry"
2096-
for name, ts in environments_summary.items()
2095+
f"{summary.name} - {time_like_to_str(summary.expiration_ts)}"
2096+
if summary.expiration_ts
2097+
else f"{summary.name} - No Expiry"
2098+
for summary in environments_summary
20972099
]
20982100
output_str = "\n".join([str(len(output)), *output])
20992101
self.log_status_update(f"Number of SQLMesh environments are: {output_str}")

sqlmesh/core/environment.py

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -95,32 +95,46 @@ def from_environment_catalog_mapping(
9595
return cls(**construction_kwargs)
9696

9797

98-
class Environment(EnvironmentNamingInfo):
99-
"""Represents an isolated environment.
100-
101-
Environments are isolated workspaces that hold pointers to physical tables.
98+
class EnvironmentSummary(PydanticModel):
99+
"""Represents summary information of an isolated environment.
102100
103101
Args:
104-
snapshots: The snapshots that are part of this environment.
102+
name: The name of the environment.
105103
start_at: The start time of the environment.
106104
end_at: The end time of the environment.
107105
plan_id: The ID of the plan that last updated this environment.
108106
previous_plan_id: The ID of the previous plan that updated this environment.
109107
expiration_ts: The timestamp when this environment will expire.
110108
finalized_ts: The timestamp when this environment was finalized.
111-
promoted_snapshot_ids: The IDs of the snapshots that are promoted in this environment
112-
(i.e. for which the views are created). If not specified, all snapshots are promoted.
113-
previous_finalized_snapshots: Snapshots that were part of this environment last time it was finalized.
114-
requirements: A mapping of library versions for all the snapshots in this environment.
115109
"""
116110

117-
snapshots_: t.List[t.Any] = Field(alias="snapshots")
111+
name: str
118112
start_at: TimeLike
119113
end_at: t.Optional[TimeLike] = None
120114
plan_id: str
121115
previous_plan_id: t.Optional[str] = None
122116
expiration_ts: t.Optional[int] = None
123117
finalized_ts: t.Optional[int] = None
118+
119+
@property
120+
def expired(self) -> bool:
121+
return self.expiration_ts is not None and self.expiration_ts <= now_timestamp()
122+
123+
124+
class Environment(EnvironmentNamingInfo, EnvironmentSummary):
125+
"""Represents an isolated environment.
126+
127+
Environments are isolated workspaces that hold pointers to physical tables.
128+
129+
Args:
130+
snapshots: The snapshots that are part of this environment.
131+
promoted_snapshot_ids: The IDs of the snapshots that are promoted in this environment
132+
(i.e. for which the views are created). If not specified, all snapshots are promoted.
133+
previous_finalized_snapshots: Snapshots that were part of this environment last time it was finalized.
134+
requirements: A mapping of library versions for all the snapshots in this environment.
135+
"""
136+
137+
snapshots_: t.List[t.Any] = Field(alias="snapshots")
124138
promoted_snapshot_ids_: t.Optional[t.List[t.Any]] = Field(
125139
default=None, alias="promoted_snapshot_ids"
126140
)
@@ -203,8 +217,16 @@ def naming_info(self) -> EnvironmentNamingInfo:
203217
)
204218

205219
@property
206-
def expired(self) -> bool:
207-
return self.expiration_ts is not None and self.expiration_ts <= now_timestamp()
220+
def summary(self) -> EnvironmentSummary:
221+
return EnvironmentSummary(
222+
name=self.name,
223+
start_at=self.start_at,
224+
end_at=self.end_at,
225+
plan_id=self.plan_id,
226+
previous_plan_id=self.previous_plan_id,
227+
expiration_ts=self.expiration_ts,
228+
finalized_ts=self.finalized_ts,
229+
)
208230

209231
def _convert_list_to_models_and_store(
210232
self, field: str, type_: t.Type[PydanticType]

sqlmesh/core/state_sync/base.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,12 @@
99
from sqlglot import __version__ as SQLGLOT_VERSION
1010

1111
from sqlmesh import migrations
12-
from sqlmesh.core.environment import Environment, EnvironmentNamingInfo, EnvironmentStatements
12+
from sqlmesh.core.environment import (
13+
Environment,
14+
EnvironmentNamingInfo,
15+
EnvironmentStatements,
16+
EnvironmentSummary,
17+
)
1318
from sqlmesh.core.snapshot import (
1419
Snapshot,
1520
SnapshotId,
@@ -137,11 +142,11 @@ def get_environments(self) -> t.List[Environment]:
137142
"""
138143

139144
@abc.abstractmethod
140-
def get_environments_summary(self) -> t.Dict[str, int]:
145+
def get_environments_summary(self) -> t.List[EnvironmentSummary]:
141146
"""Fetches all environment names along with expiry datetime.
142147
143148
Returns:
144-
A dict of all environment names along with expiry datetime.
149+
A list of all environment summaries.
145150
"""
146151

147152
@abc.abstractmethod

sqlmesh/core/state_sync/db/environment.py

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
fetchall,
1313
fetchone,
1414
)
15-
from sqlmesh.core.environment import Environment, EnvironmentStatements
15+
from sqlmesh.core.environment import Environment, EnvironmentStatements, EnvironmentSummary
1616
from sqlmesh.utils.migration import index_text_type, blob_text_type
1717
from sqlmesh.utils.date import now_timestamp, time_like_to_str
1818
from sqlmesh.utils.errors import SQLMeshError
@@ -211,18 +211,19 @@ def get_environments(self) -> t.List[Environment]:
211211
for row in fetchall(self.engine_adapter, self._environments_query())
212212
]
213213

214-
def get_environments_summary(self) -> t.Dict[str, int]:
215-
"""Fetches all environment names along with expiry datetime.
214+
def get_environments_summary(self) -> t.List[EnvironmentSummary]:
215+
"""Fetches summaries for all environments.
216216
217217
Returns:
218-
A dict of all environment names along with expiry datetime.
218+
A list of all environment summaries.
219219
"""
220-
return dict(
221-
fetchall(
220+
return [
221+
self._environment_summmary_from_row(row)
222+
for row in fetchall(
222223
self.engine_adapter,
223-
self._environments_query(required_fields=["name", "expiration_ts"]),
224-
),
225-
)
224+
self._environments_query(required_fields=list(EnvironmentSummary.all_fields())),
225+
)
226+
]
226227

227228
def get_environment(
228229
self, environment: str, lock_for_update: bool = False
@@ -286,6 +287,11 @@ def get_environment_statements(self, environment: str) -> t.List[EnvironmentStat
286287
def _environment_from_row(self, row: t.Tuple[str, ...]) -> Environment:
287288
return Environment(**{field: row[i] for i, field in enumerate(Environment.all_fields())})
288289

290+
def _environment_summmary_from_row(self, row: t.Tuple[str, ...]) -> EnvironmentSummary:
291+
return EnvironmentSummary(
292+
**{field: row[i] for i, field in enumerate(EnvironmentSummary.all_fields())}
293+
)
294+
289295
def _environments_query(
290296
self,
291297
where: t.Optional[str | exp.Expression] = None,

sqlmesh/core/state_sync/db/facade.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
from sqlmesh.core.console import Console, get_console
2828
from sqlmesh.core.engine_adapter import EngineAdapter
29-
from sqlmesh.core.environment import Environment, EnvironmentStatements
29+
from sqlmesh.core.environment import Environment, EnvironmentStatements, EnvironmentSummary
3030
from sqlmesh.core.snapshot import (
3131
Snapshot,
3232
SnapshotId,
@@ -330,11 +330,11 @@ def get_environments(self) -> t.List[Environment]:
330330
"""
331331
return self.environment_state.get_environments()
332332

333-
def get_environments_summary(self) -> t.Dict[str, int]:
333+
def get_environments_summary(self) -> t.List[EnvironmentSummary]:
334334
"""Fetches all environment names along with expiry datetime.
335335
336336
Returns:
337-
A dict of all environment names along with expiry datetime.
337+
A list of all environment summaries.
338338
"""
339339
return self.environment_state.get_environments_summary()
340340

tests/core/state_sync/test_export_import.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,11 @@ def test_import_partial(
437437
import_state(state_sync, output_file, clear=False)
438438

439439
# StateSync should have "prod", "dev" and "dev2".
440-
assert sorted(list(state_sync.get_environments_summary().keys())) == ["dev", "dev2", "prod"]
440+
assert sorted(list(env.name for env in state_sync.get_environments_summary())) == [
441+
"dev",
442+
"dev2",
443+
"prod",
444+
]
441445

442446
assert not context.plan(environment="dev", skip_tests=True).has_changes
443447
assert not context.plan(environment="dev2", skip_tests=True).has_changes

tests/core/state_sync/test_state_sync.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3464,8 +3464,8 @@ def test_get_environments_summary(
34643464
)
34653465
state_sync.promote(prod)
34663466

3467-
actual = state_sync.get_environments_summary()
3468-
expected = {"prod": None, "test_environment_a": env_a_ttl, "test_environment_b": env_b_ttl}
3467+
actual = set(state_sync.get_environments_summary())
3468+
expected = {prod.summary, env_a.summary, env_b.summary}
34693469
assert actual == expected
34703470

34713471

@@ -3493,12 +3493,12 @@ def test_get_environments_summary_only_prod(
34933493
)
34943494
state_sync.promote(prod)
34953495
actual = state_sync.get_environments_summary()
3496-
expected = {"prod": None}
3496+
expected = [prod.summary]
34973497
assert actual == expected
34983498

34993499

35003500
def test_get_environments_summary_no_env(state_sync: EngineAdapterStateSync) -> None:
3501-
assert state_sync.get_environments_summary() == {}
3501+
assert state_sync.get_environments_summary() == []
35023502

35033503

35043504
@time_machine.travel("2020-01-05 00:00:00 UTC")

0 commit comments

Comments
 (0)