Skip to content

Commit 85ecd18

Browse files
aleenprdAlin Cristian Predajochemvandooren
authored
Add Snapshot entity (#96)
Guys, I wrote some stuff that I think mostly makes sense but I can't get tests to pass if I add my snapshot fixture to the manifest i.e.. Also, I am not understanding how to try to run this code locally, and some other issues. Would much appreciate some feedback on this ``` "snapshot.package.snapshot1": { "resource_type": "snapshot", "package_name": "package", "unique_id": "snapshot.package.snapshot1", "name": "snapshot1", "description": "A great snapshot.", "original_file_path": "/path/to/snapshot1.sql", "config": {}, "meta": {}, "columns": { "a": { "name": "column_a", "description": "Column A.", "data_type": "string", "meta": {}, "constraints": [], "tags": [] } }, "constraints": [], "database": "db", "schema": "schema", "raw_code": "SELECT x FROM y", "alias": "snapshot1_alias", "patch_path": "/path/to/snapshot1.yml", "tags": [], "depends_on": {}, "language": "sql", "access": "protected", "strategy": "timestamp", "unique_key": ["a"] }, ``` --------- Co-authored-by: Alin Cristian Preda <[email protected]> Co-authored-by: Jochem van Dooren <[email protected]>
1 parent d6259ea commit 85ecd18

20 files changed

+439
-80
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ and this project adheres to
1111
- Improve documentation on rule filters. (#93)
1212
- Add `group` to model definition. (#99)
1313
- Add rule: prevent use of is_incremental() in non-incremental models. (#103)
14+
- Support linting of snapshots. (#96)
1415

1516
## [0.10.0] - 2025-01-27
1617

docs/configuration.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ The following options can be set in the `pyproject.toml` file:
5151
- `disabled_rules`: A list of rules to disable.
5252
- `fail_project_under` (default: `5.0`): If the project score is below this
5353
value the command will fail with return code 1.
54-
- `fail_any_item_under` (default: `5.0`): If any model or source scores below
55-
this value the command will fail with return code 1.
54+
- `fail_any_item_under` (default: `5.0`): If any entity scores below this value
55+
the command will fail with return code 1.
5656

5757
#### Badges configuration
5858

docs/create_rules.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# Create rules
22

3-
In order to lint and score models or sources, `dbt-score` uses a set of rules
4-
that are applied to each item. A rule can pass or fail when it is run. Based on
5-
the severity of the rule, items are scored with the weighted average of the
6-
rules results. Note that `dbt-score` comes bundled with a
3+
In order to lint and score dbt entities, `dbt-score` uses a set of rules that
4+
are applied to each item. A rule can pass or fail when it is run. Based on the
5+
severity of the rule, items are scored with the weighted average of the rules
6+
results. Note that `dbt-score` comes bundled with a
77
[set of default rules](rules/generic.md).
88

99
On top of the generic rules, it's possible to add your own rules. Two ways exist
@@ -32,7 +32,7 @@ function is its description. Therefore, it is important to use a
3232
self-explanatory name for the function and document it well.
3333

3434
The type annotation for the rule's argument dictates whether the rule should be
35-
applied to dbt models or sources.
35+
applied to dbt models, sources or snapshots.
3636

3737
Here is the same example rule, applied to sources:
3838

@@ -116,9 +116,9 @@ def sql_has_reasonable_number_of_lines(model: Model, max_lines: int = 200) -> Ru
116116

117117
### Filtering rules
118118

119-
Custom and standard rules can be configured to have filters. Filters allow
120-
models or sources to be ignored by one or multiple rules if the item doesn't
121-
satisfy the filter criteria.
119+
Custom and standard rules can be configured to have filters. Filters allow a dbt
120+
entity to be ignored by one or multiple rules if the item doesn't satisfy the
121+
filter criteria.
122122

123123
Filters are created using the same discovery mechanism and interface as custom
124124
rules, except they do not accept parameters. Similar to Python's built-in
@@ -139,8 +139,8 @@ class SkipSchemaY(RuleFilter):
139139
return model.schema.lower() != 'y'
140140
```
141141

142-
Filters also rely on type-annotations to dictate whether they apply to models or
143-
sources:
142+
Filters also rely on type-annotations to dictate whether they apply to models
143+
sources or snapshots:
144144

145145
```python
146146
from dbt_score import RuleFilter, rule_filter, Source

docs/get_started.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ It's also possible to automatically run `dbt parse`, to generate the
4040
dbt-score lint --run-dbt-parse
4141
```
4242

43-
To lint only a selection of models or sources, the argument `--select` can be
44-
used. It accepts any
43+
To lint only a selection of dbt entities, the argument `--select` can be used.
44+
It accepts any
4545
[dbt node selection syntax](https://docs.getdbt.com/reference/node-selection/syntax):
4646

4747
```shell

docs/index.md

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,18 @@
22

33
`dbt-score` is a linter for [dbt](https://www.getdbt.com/) metadata.
44

5-
dbt allows data practitioners to organize their data in to _models_ and
5+
dbt allows data practitioners to organize their data into _models_ and
66
_sources_. Those models and sources have metadata associated with them:
7-
documentation, tests, types, etc.
7+
documentation, tests, types, etc. dbt supports more types of entities, e.g.
8+
snapshots, analysis, seeds and more.
89

910
`dbt-score` allows to lint and score this metadata, in order to enforce (or
10-
encourage) good practices.
11+
encourage) good practices. The dbt entities that `dbt-score` is able to lint
12+
(currently) are:
13+
14+
- Models
15+
- Sources
16+
- Snapshots
1117

1218
## Example
1319

@@ -22,20 +28,20 @@ Score: 10.0 🥇
2228

2329
In this example, the model `customers` scores the maximum value of `10.0` as it
2430
passes all the rules. It also is awarded a golden medal because of the perfect
25-
score. By default a passing model or source with or without rule violations will
26-
not be shown, unless we pass the `--show-all` flag.
31+
score. By default a passing dbt entity with or without rule violations will not
32+
be shown, unless we pass the `--show-all` flag.
2733

2834
## Philosophy
2935

30-
dbt models/sources are often used as metadata containers: either in YAML files
31-
or through the use of `{{ config() }}` blocks, they are associated with a lot of
36+
dbt entities are often used as metadata containers: either in YAML files or
37+
through the use of `{{ config() }}` blocks, they are associated with a lot of
3238
information. At scale, it becomes tedious to enforce good practices in large
33-
data teams dealing with many models/sources.
39+
data teams dealing with many dbt entities.
3440

3541
To that end, `dbt-score` has 2 main features:
3642

37-
- It runs rules on dbt models and sources, and displays any rule violations.
38-
These can be used in interactive environments or in CI.
43+
- It runs rules on dbt entities, and displays any rule violations. These can be
44+
used in interactive environments or in CI.
3945
- Using those run results, it scores items, to ascribe them a measure of their
4046
maturity. This score can help gamify metadata improvements/coverage, and be
4147
reflected in data catalogs.

docs/programmatic_invocations.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,9 @@ When `dbt-score` terminates, it exists with one of the following exit codes:
6161
project being linted either doesn't raise any warning, or the warnings are
6262
small enough to be above the thresholds. This generally means "successful
6363
linting".
64-
- `1` in case of linting errors. This is the unhappy case: some models or
65-
sources in the project raise enough warnings to have a score below the defined
66-
thresholds. This generally means "linting doesn't pass".
64+
- `1` in case of linting errors. This is the unhappy case: some entities in the
65+
project raise enough warnings to have a score below the defined thresholds.
66+
This generally means "linting doesn't pass".
6767
- `2` in case of an unexpected error. This happens for example if something is
6868
misconfigured (for example a faulty dbt project), or the wrong parameters are
6969
given to the CLI. This generally means "setup needs to be fixed".

src/dbt_score/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
"""Init dbt_score package."""
22

3-
from dbt_score.models import Model, Source
3+
from dbt_score.models import Model, Snapshot, Source
44
from dbt_score.rule import Rule, RuleViolation, Severity, rule
55
from dbt_score.rule_filter import RuleFilter, rule_filter
66

77
__all__ = [
88
"Model",
99
"Source",
10+
"Snapshot",
1011
"RuleFilter",
1112
"Rule",
1213
"RuleViolation",

src/dbt_score/dbt_utils.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ def dbt_parse() -> "dbtRunnerResult":
6969
@dbt_required
7070
def dbt_ls(select: Iterable[str] | None) -> Iterable[str]:
7171
"""Run dbt ls."""
72-
cmd = ["ls", "--resource-types", "model", "source", "--output", "name"]
72+
cmd = ["ls", "--resource-types", "model", "source", "snapshot", "--output", "name"]
7373
if select:
7474
cmd += ["--select", *select]
7575

src/dbt_score/evaluation.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ def evaluate(self) -> None:
6161
rules = self._rule_registry.rules.values()
6262

6363
for evaluable in chain(
64-
self._manifest_loader.models, self._manifest_loader.sources
64+
self._manifest_loader.models,
65+
self._manifest_loader.sources,
66+
self._manifest_loader.snapshots,
6567
):
6668
# type inference on elements from `chain` is wonky
6769
# and resolves to superclass HasColumnsMixin
@@ -91,5 +93,9 @@ def evaluate(self) -> None:
9193
)
9294

9395
# Add null check before calling project_evaluated
94-
if self._manifest_loader.models or self._manifest_loader.sources:
96+
if (
97+
self._manifest_loader.models
98+
or self._manifest_loader.sources
99+
or self._manifest_loader.snapshots
100+
):
95101
self._formatter.project_evaluated(self.project_score)

src/dbt_score/formatters/human_readable_formatter.py

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from dbt_score.evaluation import EvaluableResultsType
66
from dbt_score.formatters import Formatter
7-
from dbt_score.models import Evaluable, Model, Source
7+
from dbt_score.models import Evaluable, Model, Snapshot, Source
88
from dbt_score.rule import RuleViolation
99
from dbt_score.scoring import Score
1010

@@ -35,6 +35,8 @@ def pretty_name(evaluable: Evaluable) -> str:
3535
return evaluable.name
3636
case Source():
3737
return evaluable.selector_name
38+
case Snapshot():
39+
return evaluable.name
3840
case _:
3941
raise NotImplementedError
4042

@@ -53,7 +55,7 @@ def evaluable_evaluated(
5355
)
5456
):
5557
resource_type = type(evaluable).__name__
56-
name_formatted = f"{resource_type[0]}: {self.pretty_name(evaluable)}"
58+
name_formatted = f"{resource_type}: {self.pretty_name(evaluable)}"
5759
header = (
5860
f"{score.badge} "
5961
f"{self.bold(name_formatted)} (score: {score.rounded_value!s})"
@@ -72,8 +74,7 @@ def evaluable_evaluated(
7274
)
7375
else:
7476
print(
75-
f"{self.indent}{self.label_error} {rule.source()}: "
76-
f"{result!s}"
77+
f"{self.indent}{self.label_error} {rule.source()}: {result!s}"
7778
)
7879
print()
7980

0 commit comments

Comments
 (0)