dbt-bouncer 1.31.2rc3__py3-none-any.whl → 2.0.0rc1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- dbt_bouncer/artifact_parsers/dbt_cloud/catalog_latest.py +21 -21
- dbt_bouncer/artifact_parsers/dbt_cloud/manifest_latest.py +1745 -1745
- dbt_bouncer/artifact_parsers/dbt_cloud/run_results_latest.py +22 -22
- dbt_bouncer/artifact_parsers/parsers_catalog.py +26 -24
- dbt_bouncer/artifact_parsers/parsers_common.py +57 -36
- dbt_bouncer/artifact_parsers/parsers_manifest.py +98 -69
- dbt_bouncer/artifact_parsers/parsers_run_results.py +32 -19
- dbt_bouncer/check_base.py +22 -11
- dbt_bouncer/checks/catalog/check_catalog_sources.py +22 -12
- dbt_bouncer/checks/catalog/check_columns.py +175 -105
- dbt_bouncer/checks/common.py +24 -3
- dbt_bouncer/checks/manifest/check_exposures.py +79 -52
- dbt_bouncer/checks/manifest/check_lineage.py +69 -40
- dbt_bouncer/checks/manifest/check_macros.py +177 -104
- dbt_bouncer/checks/manifest/check_metadata.py +28 -18
- dbt_bouncer/checks/manifest/check_models.py +842 -496
- dbt_bouncer/checks/manifest/check_seeds.py +63 -0
- dbt_bouncer/checks/manifest/check_semantic_models.py +28 -20
- dbt_bouncer/checks/manifest/check_snapshots.py +57 -33
- dbt_bouncer/checks/manifest/check_sources.py +246 -137
- dbt_bouncer/checks/manifest/check_unit_tests.py +97 -54
- dbt_bouncer/checks/run_results/check_run_results.py +34 -20
- dbt_bouncer/config_file_parser.py +47 -28
- dbt_bouncer/config_file_validator.py +11 -8
- dbt_bouncer/global_context.py +31 -0
- dbt_bouncer/main.py +128 -67
- dbt_bouncer/runner.py +61 -31
- dbt_bouncer/utils.py +146 -50
- dbt_bouncer/version.py +1 -1
- {dbt_bouncer-1.31.2rc3.dist-info → dbt_bouncer-2.0.0rc1.dist-info}/METADATA +15 -15
- dbt_bouncer-2.0.0rc1.dist-info/RECORD +37 -0
- dbt_bouncer-1.31.2rc3.dist-info/RECORD +0 -35
- {dbt_bouncer-1.31.2rc3.dist-info → dbt_bouncer-2.0.0rc1.dist-info}/WHEEL +0 -0
- {dbt_bouncer-1.31.2rc3.dist-info → dbt_bouncer-2.0.0rc1.dist-info}/entry_points.txt +0 -0
- {dbt_bouncer-1.31.2rc3.dist-info → dbt_bouncer-2.0.0rc1.dist-info}/licenses/LICENSE +0 -0
- {dbt_bouncer-1.31.2rc3.dist-info → dbt_bouncer-2.0.0rc1.dist-info}/top_level.txt +0 -0
|
@@ -1,34 +1,33 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
from typing import TYPE_CHECKING, List, Literal
|
|
1
|
+
from typing import TYPE_CHECKING, Literal
|
|
5
2
|
|
|
6
3
|
from pydantic import Field
|
|
7
4
|
|
|
8
5
|
from dbt_bouncer.check_base import BaseCheck
|
|
9
6
|
|
|
10
7
|
if TYPE_CHECKING:
|
|
11
|
-
from dbt_bouncer.artifact_parsers.
|
|
8
|
+
from dbt_bouncer.artifact_parsers.parsers_manifest import (
|
|
12
9
|
DbtBouncerExposureBase,
|
|
13
10
|
DbtBouncerModelBase,
|
|
14
11
|
)
|
|
15
12
|
|
|
13
|
+
from dbt_bouncer.checks.common import DbtBouncerFailedCheckError
|
|
14
|
+
|
|
16
15
|
|
|
17
16
|
class CheckExposureOnModel(BaseCheck):
|
|
18
17
|
"""Exposures should depend on a model.
|
|
19
18
|
|
|
20
19
|
Parameters:
|
|
21
|
-
maximum_number_of_models (
|
|
22
|
-
minimum_number_of_models (
|
|
20
|
+
maximum_number_of_models (int | None): The maximum number of models an exposure can depend on, defaults to 100.
|
|
21
|
+
minimum_number_of_models (int | None): The minimum number of models an exposure can depend on, defaults to 1.
|
|
23
22
|
|
|
24
23
|
Receives:
|
|
25
24
|
exposure (DbtBouncerExposureBase): The DbtBouncerExposureBase object to check.
|
|
26
25
|
|
|
27
26
|
Other Parameters:
|
|
28
|
-
description (
|
|
29
|
-
exclude (
|
|
30
|
-
include (
|
|
31
|
-
severity (
|
|
27
|
+
description (str | None): Description of what the check does and why it is implemented.
|
|
28
|
+
exclude (str | None): Regex pattern to match the exposure path (i.e the .yml file where the exposure is configured). Exposure paths that match the pattern will not be checked.
|
|
29
|
+
include (str | None): Regex pattern to match the exposure path (i.e the .yml file where the exposure is configured). Only exposure paths that match the pattern will be checked.
|
|
30
|
+
severity (Literal["error", "warn"] | None): Severity level of the check. Default: `error`.
|
|
32
31
|
|
|
33
32
|
Example(s):
|
|
34
33
|
```yaml
|
|
@@ -44,35 +43,47 @@ class CheckExposureOnModel(BaseCheck):
|
|
|
44
43
|
|
|
45
44
|
"""
|
|
46
45
|
|
|
47
|
-
exposure: "DbtBouncerExposureBase" = Field(default=None)
|
|
46
|
+
exposure: "DbtBouncerExposureBase | None" = Field(default=None)
|
|
48
47
|
maximum_number_of_models: int = Field(default=100)
|
|
49
48
|
minimum_number_of_models: int = Field(default=1)
|
|
50
49
|
name: Literal["check_exposure_based_on_model"]
|
|
51
50
|
|
|
52
51
|
def execute(self) -> None:
|
|
53
|
-
"""Execute the check.
|
|
54
|
-
number_of_upstream_models = len(self.exposure.depends_on.nodes)
|
|
52
|
+
"""Execute the check.
|
|
55
53
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
54
|
+
Raises:
|
|
55
|
+
DbtBouncerFailedCheckError: If upstream models number is not within limits.
|
|
56
|
+
|
|
57
|
+
"""
|
|
58
|
+
if self.exposure is None:
|
|
59
|
+
raise DbtBouncerFailedCheckError("self.exposure is None")
|
|
60
|
+
depends_on = self.exposure.depends_on
|
|
61
|
+
number_of_upstream_models = (
|
|
62
|
+
len(getattr(depends_on, "nodes", []) or []) if depends_on else 0
|
|
61
63
|
)
|
|
62
64
|
|
|
65
|
+
if number_of_upstream_models < self.minimum_number_of_models:
|
|
66
|
+
raise DbtBouncerFailedCheckError(
|
|
67
|
+
f"`{self.exposure.name}` is based on less models ({number_of_upstream_models}) than the minimum permitted ({self.minimum_number_of_models})."
|
|
68
|
+
)
|
|
69
|
+
if number_of_upstream_models > self.maximum_number_of_models:
|
|
70
|
+
raise DbtBouncerFailedCheckError(
|
|
71
|
+
f"`{self.exposure.name}` is based on more models ({number_of_upstream_models}) than the maximum permitted ({self.maximum_number_of_models})."
|
|
72
|
+
)
|
|
73
|
+
|
|
63
74
|
|
|
64
75
|
class CheckExposureOnNonPublicModels(BaseCheck):
|
|
65
76
|
"""Exposures should be based on public models only.
|
|
66
77
|
|
|
67
78
|
Receives:
|
|
68
79
|
exposure (DbtBouncerExposureBase): The DbtBouncerExposureBase object to check.
|
|
69
|
-
models (
|
|
80
|
+
models (list[DbtBouncerModelBase]): List of DbtBouncerModelBase objects parsed from `manifest.json`.
|
|
70
81
|
|
|
71
82
|
Other Parameters:
|
|
72
|
-
description (
|
|
73
|
-
exclude (
|
|
74
|
-
include (
|
|
75
|
-
severity (
|
|
83
|
+
description (str | None): Description of what the check does and why it is implemented.
|
|
84
|
+
exclude (str | None): Regex pattern to match the exposure path (i.e the .yml file where the exposure is configured). Exposure paths that match the pattern will not be checked.
|
|
85
|
+
include (str | None): Regex pattern to match the exposure path (i.e the .yml file where the exposure is configured). Only exposure paths that match the pattern will be checked.
|
|
86
|
+
severity (Literal["error", "warn"] | None): Severity level of the check. Default: `error`.
|
|
76
87
|
|
|
77
88
|
Example(s):
|
|
78
89
|
```yaml
|
|
@@ -82,44 +93,52 @@ class CheckExposureOnNonPublicModels(BaseCheck):
|
|
|
82
93
|
|
|
83
94
|
"""
|
|
84
95
|
|
|
85
|
-
exposure: "DbtBouncerExposureBase" = Field(default=None)
|
|
86
|
-
models:
|
|
96
|
+
exposure: "DbtBouncerExposureBase | None" = Field(default=None)
|
|
97
|
+
models: list["DbtBouncerModelBase"] = Field(default=[])
|
|
87
98
|
name: Literal["check_exposure_based_on_non_public_models"]
|
|
88
99
|
|
|
89
100
|
def execute(self) -> None:
|
|
90
|
-
"""Execute the check.
|
|
101
|
+
"""Execute the check.
|
|
102
|
+
|
|
103
|
+
Raises:
|
|
104
|
+
DbtBouncerFailedCheckError: If exposure is based on non-public models.
|
|
105
|
+
|
|
106
|
+
"""
|
|
107
|
+
if self.exposure is None:
|
|
108
|
+
raise DbtBouncerFailedCheckError("self.exposure is None")
|
|
91
109
|
non_public_upstream_dependencies = []
|
|
92
|
-
for model in self.exposure.depends_on
|
|
110
|
+
for model in getattr(self.exposure.depends_on, "nodes", []) or []:
|
|
93
111
|
if (
|
|
94
112
|
next(m for m in self.models if m.unique_id == model).resource_type
|
|
95
113
|
== "model"
|
|
96
114
|
and next(m for m in self.models if m.unique_id == model).package_name
|
|
97
115
|
== self.exposure.package_name
|
|
98
116
|
):
|
|
99
|
-
|
|
100
|
-
if
|
|
101
|
-
non_public_upstream_dependencies.append(
|
|
117
|
+
model_obj = next(m for m in self.models if m.unique_id == model)
|
|
118
|
+
if model_obj.access and model_obj.access.value != "public":
|
|
119
|
+
non_public_upstream_dependencies.append(model_obj.name)
|
|
102
120
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
121
|
+
if non_public_upstream_dependencies:
|
|
122
|
+
raise DbtBouncerFailedCheckError(
|
|
123
|
+
f"`{self.exposure.name}` is based on a model(s) that is not public: {non_public_upstream_dependencies}."
|
|
124
|
+
)
|
|
106
125
|
|
|
107
126
|
|
|
108
127
|
class CheckExposureOnView(BaseCheck):
|
|
109
128
|
"""Exposures should not be based on views.
|
|
110
129
|
|
|
111
130
|
Parameters:
|
|
112
|
-
materializations_to_include (
|
|
131
|
+
materializations_to_include (list[str] | None): List of materializations to include in the check.
|
|
113
132
|
|
|
114
133
|
Receives:
|
|
115
134
|
exposure (DbtBouncerExposureBase): The DbtBouncerExposureBase object to check.
|
|
116
|
-
models (
|
|
135
|
+
models (list[DbtBouncerModelBase]): List of DbtBouncerModelBase objects parsed from `manifest.json`.
|
|
117
136
|
|
|
118
137
|
Other Parameters:
|
|
119
|
-
description (
|
|
120
|
-
exclude (
|
|
121
|
-
include (
|
|
122
|
-
severity (
|
|
138
|
+
description (str | None): Description of what the check does and why it is implemented.
|
|
139
|
+
exclude (str | None): Regex pattern to match the exposure path (i.e the .yml file where the exposure is configured). Exposure paths that match the pattern will not be checked.
|
|
140
|
+
include (str | None): Regex pattern to match the exposure path (i.e the .yml file where the exposure is configured). Only exposure paths that match the pattern will be checked.
|
|
141
|
+
severity (Literal["error", "warn"] | None): Severity level of the check. Default: `error`.
|
|
123
142
|
|
|
124
143
|
Example(s):
|
|
125
144
|
```yaml
|
|
@@ -137,27 +156,35 @@ class CheckExposureOnView(BaseCheck):
|
|
|
137
156
|
|
|
138
157
|
"""
|
|
139
158
|
|
|
140
|
-
exposure: "DbtBouncerExposureBase" = Field(default=None)
|
|
141
|
-
materializations_to_include:
|
|
159
|
+
exposure: "DbtBouncerExposureBase | None" = Field(default=None)
|
|
160
|
+
materializations_to_include: list[str] = Field(
|
|
142
161
|
default=["ephemeral", "view"],
|
|
143
162
|
)
|
|
144
|
-
models:
|
|
163
|
+
models: list["DbtBouncerModelBase"] = Field(default=[])
|
|
145
164
|
name: Literal["check_exposure_based_on_view"]
|
|
146
165
|
|
|
147
166
|
def execute(self) -> None:
|
|
148
|
-
"""Execute the check.
|
|
167
|
+
"""Execute the check.
|
|
168
|
+
|
|
169
|
+
Raises:
|
|
170
|
+
DbtBouncerFailedCheckError: If exposure is based on a model that is not a table.
|
|
171
|
+
|
|
172
|
+
"""
|
|
173
|
+
if self.exposure is None:
|
|
174
|
+
raise DbtBouncerFailedCheckError("self.exposure is None")
|
|
149
175
|
non_table_upstream_dependencies = []
|
|
150
|
-
for model in self.exposure.depends_on
|
|
176
|
+
for model in getattr(self.exposure.depends_on, "nodes", []) or []:
|
|
151
177
|
if (
|
|
152
178
|
next(m for m in self.models if m.unique_id == model).resource_type
|
|
153
179
|
== "model"
|
|
154
180
|
and next(m for m in self.models if m.unique_id == model).package_name
|
|
155
181
|
== self.exposure.package_name
|
|
156
182
|
):
|
|
157
|
-
|
|
158
|
-
if
|
|
159
|
-
non_table_upstream_dependencies.append(
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
183
|
+
model_obj = next(m for m in self.models if m.unique_id == model)
|
|
184
|
+
if model_obj.config.materialized in self.materializations_to_include:
|
|
185
|
+
non_table_upstream_dependencies.append(model_obj.name)
|
|
186
|
+
|
|
187
|
+
if non_table_upstream_dependencies:
|
|
188
|
+
raise DbtBouncerFailedCheckError(
|
|
189
|
+
f"`{self.exposure.name}` is based on a model that is not a table: {non_table_upstream_dependencies}."
|
|
190
|
+
)
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
# mypy: disable-error-code="union-attr"
|
|
2
|
-
|
|
3
1
|
import re
|
|
4
|
-
from typing import TYPE_CHECKING,
|
|
2
|
+
from typing import TYPE_CHECKING, Literal
|
|
5
3
|
|
|
6
4
|
from dbt_bouncer.check_base import BaseCheck
|
|
7
5
|
|
|
8
6
|
if TYPE_CHECKING:
|
|
9
|
-
from dbt_bouncer.artifact_parsers.
|
|
7
|
+
from dbt_bouncer.artifact_parsers.parsers_manifest import (
|
|
10
8
|
DbtBouncerManifest,
|
|
11
9
|
DbtBouncerModelBase,
|
|
12
10
|
)
|
|
13
11
|
|
|
14
12
|
from pydantic import Field
|
|
15
13
|
|
|
14
|
+
from dbt_bouncer.checks.common import DbtBouncerFailedCheckError
|
|
16
15
|
from dbt_bouncer.utils import clean_path_str, get_clean_model_name
|
|
17
16
|
|
|
18
17
|
|
|
@@ -25,13 +24,13 @@ class CheckLineagePermittedUpstreamModels(BaseCheck):
|
|
|
25
24
|
Receives:
|
|
26
25
|
manifest_obj (DbtBouncerManifest): The manifest object.
|
|
27
26
|
model (DbtBouncerModelBase): The DbtBouncerModelBase object to check.
|
|
28
|
-
models (
|
|
27
|
+
models (list[DbtBouncerModelBase]): List of DbtBouncerModelBase objects parsed from `manifest.json`.
|
|
29
28
|
|
|
30
29
|
Other Parameters:
|
|
31
|
-
description (
|
|
32
|
-
exclude (
|
|
33
|
-
include (
|
|
34
|
-
severity (
|
|
30
|
+
description (str | None): Description of what the check does and why it is implemented.
|
|
31
|
+
exclude (str | None): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
|
|
32
|
+
include (str | None): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
|
|
33
|
+
severity (Literal["error", "warn"] | None): Severity level of the check. Default: `error`.
|
|
35
34
|
|
|
36
35
|
Example(s):
|
|
37
36
|
```yaml
|
|
@@ -49,18 +48,27 @@ class CheckLineagePermittedUpstreamModels(BaseCheck):
|
|
|
49
48
|
|
|
50
49
|
"""
|
|
51
50
|
|
|
52
|
-
manifest_obj: "DbtBouncerManifest" = Field(default=None)
|
|
53
|
-
model: "DbtBouncerModelBase" = Field(default=None)
|
|
54
|
-
models:
|
|
51
|
+
manifest_obj: "DbtBouncerManifest | None" = Field(default=None)
|
|
52
|
+
model: "DbtBouncerModelBase | None" = Field(default=None)
|
|
53
|
+
models: list["DbtBouncerModelBase"] = Field(default=[])
|
|
55
54
|
name: Literal["check_lineage_permitted_upstream_models"]
|
|
56
|
-
package_name:
|
|
55
|
+
package_name: str | None = Field(default=None)
|
|
57
56
|
upstream_path_pattern: str
|
|
58
57
|
|
|
59
58
|
def execute(self) -> None:
|
|
60
|
-
"""Execute the check.
|
|
59
|
+
"""Execute the check.
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
DbtBouncerFailedCheckError: If upstream models are not permitted.
|
|
63
|
+
|
|
64
|
+
"""
|
|
65
|
+
if self.model is None:
|
|
66
|
+
raise DbtBouncerFailedCheckError("self.model is None")
|
|
67
|
+
if self.manifest_obj is None:
|
|
68
|
+
raise DbtBouncerFailedCheckError("self.manifest_obj is None")
|
|
61
69
|
upstream_models = [
|
|
62
70
|
x
|
|
63
|
-
for x in self.model.depends_on
|
|
71
|
+
for x in getattr(self.model.depends_on, "nodes", []) or []
|
|
64
72
|
if x.split(".")[0] == "model"
|
|
65
73
|
and x.split(".")[1]
|
|
66
74
|
== (self.package_name or self.manifest_obj.manifest.metadata.project_name)
|
|
@@ -77,9 +85,10 @@ class CheckLineagePermittedUpstreamModels(BaseCheck):
|
|
|
77
85
|
)
|
|
78
86
|
is None
|
|
79
87
|
]
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
88
|
+
if not_permitted_upstream_models:
|
|
89
|
+
raise DbtBouncerFailedCheckError(
|
|
90
|
+
f"`{get_clean_model_name(self.model.unique_id)}` references upstream models that are not permitted: {[m.split('.')[-1] for m in not_permitted_upstream_models]}."
|
|
91
|
+
)
|
|
83
92
|
|
|
84
93
|
|
|
85
94
|
class CheckLineageSeedCannotBeUsed(BaseCheck):
|
|
@@ -89,10 +98,10 @@ class CheckLineageSeedCannotBeUsed(BaseCheck):
|
|
|
89
98
|
model (DbtBouncerModelBase): The DbtBouncerModelBase object to check.
|
|
90
99
|
|
|
91
100
|
Other Parameters:
|
|
92
|
-
description (
|
|
93
|
-
exclude (
|
|
94
|
-
include (
|
|
95
|
-
severity (
|
|
101
|
+
description (str | None): Description of what the check does and why it is implemented.
|
|
102
|
+
exclude (str | None): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
|
|
103
|
+
include (str | None): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
|
|
104
|
+
severity (Literal["error", "warn"] | None): Severity level of the check. Default: `error`.
|
|
96
105
|
|
|
97
106
|
Example(s):
|
|
98
107
|
```yaml
|
|
@@ -103,16 +112,26 @@ class CheckLineageSeedCannotBeUsed(BaseCheck):
|
|
|
103
112
|
|
|
104
113
|
"""
|
|
105
114
|
|
|
106
|
-
model: "DbtBouncerModelBase" = Field(default=None)
|
|
115
|
+
model: "DbtBouncerModelBase | None" = Field(default=None)
|
|
107
116
|
name: Literal["check_lineage_seed_cannot_be_used"]
|
|
108
117
|
|
|
109
118
|
def execute(self) -> None:
|
|
110
|
-
"""Execute the check.
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
119
|
+
"""Execute the check.
|
|
120
|
+
|
|
121
|
+
Raises:
|
|
122
|
+
DbtBouncerFailedCheckError: If seed is referenced.
|
|
123
|
+
|
|
124
|
+
"""
|
|
125
|
+
if self.model is None:
|
|
126
|
+
raise DbtBouncerFailedCheckError("self.model is None")
|
|
127
|
+
if [
|
|
128
|
+
x
|
|
129
|
+
for x in getattr(self.model.depends_on, "nodes", []) or []
|
|
130
|
+
if x.split(".")[0] == "seed"
|
|
131
|
+
]:
|
|
132
|
+
raise DbtBouncerFailedCheckError(
|
|
133
|
+
f"`{get_clean_model_name(self.model.unique_id)}` references a seed even though this is not permitted."
|
|
134
|
+
)
|
|
116
135
|
|
|
117
136
|
|
|
118
137
|
class CheckLineageSourceCannotBeUsed(BaseCheck):
|
|
@@ -122,10 +141,10 @@ class CheckLineageSourceCannotBeUsed(BaseCheck):
|
|
|
122
141
|
model (DbtBouncerModelBase): The DbtBouncerModelBase object to check.
|
|
123
142
|
|
|
124
143
|
Other Parameters:
|
|
125
|
-
description (
|
|
126
|
-
exclude (
|
|
127
|
-
include (
|
|
128
|
-
severity (
|
|
144
|
+
description (str | None): Description of what the check does and why it is implemented.
|
|
145
|
+
exclude (str | None): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
|
|
146
|
+
include (str | None): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
|
|
147
|
+
severity (Literal["error", "warn"] | None): Severity level of the check. Default: `error`.
|
|
129
148
|
|
|
130
149
|
Example(s):
|
|
131
150
|
```yaml
|
|
@@ -136,13 +155,23 @@ class CheckLineageSourceCannotBeUsed(BaseCheck):
|
|
|
136
155
|
|
|
137
156
|
"""
|
|
138
157
|
|
|
139
|
-
model: "DbtBouncerModelBase" = Field(default=None)
|
|
158
|
+
model: "DbtBouncerModelBase | None" = Field(default=None)
|
|
140
159
|
name: Literal["check_lineage_source_cannot_be_used"]
|
|
141
160
|
|
|
142
161
|
def execute(self) -> None:
|
|
143
|
-
"""Execute the check.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
162
|
+
"""Execute the check.
|
|
163
|
+
|
|
164
|
+
Raises:
|
|
165
|
+
DbtBouncerFailedCheckError: If source is referenced.
|
|
166
|
+
|
|
167
|
+
"""
|
|
168
|
+
if self.model is None:
|
|
169
|
+
raise DbtBouncerFailedCheckError("self.model is None")
|
|
170
|
+
if [
|
|
171
|
+
x
|
|
172
|
+
for x in getattr(self.model.depends_on, "nodes", []) or []
|
|
173
|
+
if x.split(".")[0] == "source"
|
|
174
|
+
]:
|
|
175
|
+
raise DbtBouncerFailedCheckError(
|
|
176
|
+
f"`{get_clean_model_name(self.model.unique_id)}` references a source even though this is not permitted."
|
|
177
|
+
)
|