dbt-bouncer 0.14.0__tar.gz

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.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Pádraic Slattery
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,196 @@
1
+ Metadata-Version: 2.1
2
+ Name: dbt-bouncer
3
+ Version: 0.14.0
4
+ Summary: Configure and enforce conventions for your dbt project.
5
+ Home-page: https://github.com/godatadriven/dbt-bouncer
6
+ License: MIT
7
+ Keywords: python,cli,dbt,CI/CD
8
+ Author: Padraic Slattery
9
+ Author-email: pgoslatara@gmail.com
10
+ Maintainer: Padraic Slattery
11
+ Maintainer-email: pgoslatara@gmail.com
12
+ Requires-Python: >=3.8.1,<3.13
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3.9
17
+ Classifier: Programming Language :: Python :: 3.10
18
+ Classifier: Programming Language :: Python :: 3.11
19
+ Classifier: Programming Language :: Python :: 3.12
20
+ Classifier: Programming Language :: Python :: 3 :: Only
21
+ Classifier: Programming Language :: Python :: 3.8
22
+ Requires-Dist: click (<9)
23
+ Requires-Dist: dbt-artifacts-parser (>=0,<1)
24
+ Requires-Dist: pydantic (>=2,<3)
25
+ Requires-Dist: pytest (<9)
26
+ Requires-Dist: pyyaml (<7)
27
+ Requires-Dist: requests (>=2,<3)
28
+ Requires-Dist: tabulate (>=0,<1)
29
+ Project-URL: Repository, https://github.com/godatadriven/dbt-bouncer
30
+ Description-Content-Type: text/markdown
31
+
32
+ <p align="center">
33
+ <img src="./images/logo.webp" alt="dbt-bouncer logo" width="500"/>
34
+ </p>
35
+
36
+
37
+ <h1 align="center">
38
+ dbt-bouncer
39
+ </h1>
40
+ <h2 align="center">
41
+ Configure and enforce conventions for your dbt project.
42
+ </h2>
43
+
44
+ <div align="center">
45
+ <a>
46
+ <img src="https://img.shields.io/github/release/godatadriven/dbt-bouncer.svg?logo=github">
47
+ </a>
48
+ <a>
49
+ <img src="https://github.com/godatadriven/dbt-bouncer/actions/workflows/ci_pipeline.yml/badge.svg">
50
+ </a>
51
+ <a>
52
+ <img src="https://img.shields.io/badge/License-MIT-yellow.svg">
53
+ </a>
54
+ <a>
55
+ <img src="https://img.shields.io/github/last-commit/godatadriven/dbt-bouncer/main">
56
+ </a>
57
+ <a>
58
+ <img src="https://img.shields.io/github/commits-since/godatadriven/dbt-bouncer/latest">
59
+ </a>
60
+ </div>
61
+
62
+ <div align="center">
63
+ <a>
64
+ <img alt="dbt" src="https://img.shields.io/badge/dbt%20-%3E%3D1.6-333?logo=dbt">
65
+ </a>
66
+ <a>
67
+ <img alt="Docker Supported" src="https://img.shields.io/badge/Docker%20-Supported-0db7ed?logo=docker">
68
+ </a>
69
+ <a>
70
+ <img alt="GitHub Supported" src="https://img.shields.io/badge/GitHub%20-Supported-333?logo=github">
71
+ </a>
72
+ </div>
73
+
74
+ <div align="center">
75
+ <a>
76
+ <img src="https://img.shields.io/badge/code%20style-black-000000.svg">
77
+ </a>
78
+ <a>
79
+ <img src="https://www.aschey.tech/tokei/github/godatadriven/dbt-bouncer?category=code">
80
+ </a>
81
+ </div>
82
+ <br/>
83
+
84
+ # How to use
85
+
86
+ 1. Generate a `manifest.json` by running `dbt parse`.
87
+ 1. Create a `dbt-bouncer.yml` config file, details [here](#config-file).
88
+ 1. Run `dbt-bouncer` to validate that your conventions are being maintained. You can use GitHub Actions, Docker, a `.pex` file or python to run `dbt-bouncer`.
89
+
90
+ ## GitHub Actions
91
+
92
+ ```yaml
93
+ steps:
94
+ ...
95
+
96
+ - uses: godatadriven/dbt-bouncer@v0
97
+ with:
98
+ config-file: ./<PATH_TO_CONFIG_FILE>
99
+ send-pr-comment: true # optional, defaults to true
100
+
101
+ ...
102
+ ```
103
+
104
+ ## Docker
105
+
106
+ Don't use GitHub Actions? You can still use dbt-bouncer via Docker:
107
+
108
+ ```bash
109
+ docker run --rm \
110
+ --volume "$PWD":/app \
111
+ ghcr.io/godatadriven/dbt-bouncer:vX.X.X \
112
+ --config-file /app/<PATH_TO_CONFIG_FILE>
113
+ ```
114
+
115
+ ## Pex
116
+
117
+ You can also run the `.pex` ([Python EXecutable](https://docs.pex-tool.org/whatispex.html#whatispex)) artifact directly once you have a python executable installed:
118
+
119
+ ```bash
120
+ wget https://github.com/godatadriven/dbt-bouncer/releases/download/vX.X.X/dbt-bouncer.pex -O dbt-bouncer.pex
121
+
122
+ dbt-bouncer.pex --config-file $PWD/<PATH_TO_CONFIG_FILE>
123
+ ```
124
+
125
+ ## Python
126
+
127
+ Install from [pypi.org](https://pypi.org/dbt-bouncer):
128
+
129
+ ```shell
130
+ pip install dbt-bouncer
131
+ ```
132
+
133
+ Run:
134
+ ```shell
135
+ dbt-bouncer.pex --config-file $PWD/<PATH_TO_CONFIG_FILE>
136
+ ``
137
+
138
+ # Config file
139
+
140
+ `dbt-bouncer` requires a config file to be provided. This file configures what checks are run. Here is an example config file:
141
+
142
+ ```yaml
143
+ dbt-artifacts-dir: target # [Optional] Directory where the dbt artifacts exists, generally the `target` directory inside a dbt project. Defaults to `./target`.
144
+
145
+ manifest_checks:
146
+ - name: check_macro_name_matches_file_name
147
+ - name: check_model_names
148
+ include: ^staging
149
+ model_name_pattern: ^stg_
150
+ ```
151
+
152
+ # Checks
153
+
154
+ :bulb: Click on a check name to see more details.
155
+
156
+ **Macros**
157
+
158
+ * [`check_macro_arguments_description_populated`](./dbt_bouncer/checks/checks.md#check_macro_arguments_description_populated): Macro arguments must have a populated description.
159
+ * [`check_macro_description_populated`](./dbt_bouncer/checks/checks.md#check_macro_description_populated): Macros must have a populated description.
160
+ * [`check_macro_name_matches_file_name`](./dbt_bouncer/checks/checks.md#check_macro_name_matches_file_name): Macros names must be the same as the file they are contained in.
161
+
162
+ **Metadata**
163
+
164
+ * [`check_project_name`](./dbt_bouncer/checks/checks.md#check_project_name): Enforce that the name of the dbt project matches a supplied regex.
165
+
166
+ **Miscellaneous**
167
+
168
+ * [`check_top_level_directories`](./dbt_bouncer/checks/checks.md#check_top_level_directories): Only specified top-level directories are allowed to contain models.
169
+
170
+ **Models**
171
+
172
+ * [`check_model_description_populated`](./dbt_bouncer/checks/checks.md#check_model_description_populated): Models must have a populated description.
173
+ * [`check_model_names`](./dbt_bouncer/checks/checks.md#check_model_names): Models must have a name that matches the supplied regex.
174
+
175
+ **Sources**
176
+
177
+ * [`check_source_has_meta_keys`](./dbt_bouncer/checks/checks.md#check_source_has_meta_keys): The `meta` config for sources must have the specified keys.
178
+
179
+ # Development
180
+
181
+ To setup your development environment, fork this repository and run:
182
+
183
+ ```bash
184
+ poetry shell
185
+ poetry install
186
+ pre-commit install
187
+ ```
188
+
189
+ Set required environment variables by copying `.env.example` to `.env` and updating the values.
190
+
191
+ All tests can be run via:
192
+ ```bash
193
+ make build-artifacts # Rebuilds dbt artifacts used by pytest
194
+ make test
195
+ ```
196
+
@@ -0,0 +1,164 @@
1
+ <p align="center">
2
+ <img src="./images/logo.webp" alt="dbt-bouncer logo" width="500"/>
3
+ </p>
4
+
5
+
6
+ <h1 align="center">
7
+ dbt-bouncer
8
+ </h1>
9
+ <h2 align="center">
10
+ Configure and enforce conventions for your dbt project.
11
+ </h2>
12
+
13
+ <div align="center">
14
+ <a>
15
+ <img src="https://img.shields.io/github/release/godatadriven/dbt-bouncer.svg?logo=github">
16
+ </a>
17
+ <a>
18
+ <img src="https://github.com/godatadriven/dbt-bouncer/actions/workflows/ci_pipeline.yml/badge.svg">
19
+ </a>
20
+ <a>
21
+ <img src="https://img.shields.io/badge/License-MIT-yellow.svg">
22
+ </a>
23
+ <a>
24
+ <img src="https://img.shields.io/github/last-commit/godatadriven/dbt-bouncer/main">
25
+ </a>
26
+ <a>
27
+ <img src="https://img.shields.io/github/commits-since/godatadriven/dbt-bouncer/latest">
28
+ </a>
29
+ </div>
30
+
31
+ <div align="center">
32
+ <a>
33
+ <img alt="dbt" src="https://img.shields.io/badge/dbt%20-%3E%3D1.6-333?logo=dbt">
34
+ </a>
35
+ <a>
36
+ <img alt="Docker Supported" src="https://img.shields.io/badge/Docker%20-Supported-0db7ed?logo=docker">
37
+ </a>
38
+ <a>
39
+ <img alt="GitHub Supported" src="https://img.shields.io/badge/GitHub%20-Supported-333?logo=github">
40
+ </a>
41
+ </div>
42
+
43
+ <div align="center">
44
+ <a>
45
+ <img src="https://img.shields.io/badge/code%20style-black-000000.svg">
46
+ </a>
47
+ <a>
48
+ <img src="https://www.aschey.tech/tokei/github/godatadriven/dbt-bouncer?category=code">
49
+ </a>
50
+ </div>
51
+ <br/>
52
+
53
+ # How to use
54
+
55
+ 1. Generate a `manifest.json` by running `dbt parse`.
56
+ 1. Create a `dbt-bouncer.yml` config file, details [here](#config-file).
57
+ 1. Run `dbt-bouncer` to validate that your conventions are being maintained. You can use GitHub Actions, Docker, a `.pex` file or python to run `dbt-bouncer`.
58
+
59
+ ## GitHub Actions
60
+
61
+ ```yaml
62
+ steps:
63
+ ...
64
+
65
+ - uses: godatadriven/dbt-bouncer@v0
66
+ with:
67
+ config-file: ./<PATH_TO_CONFIG_FILE>
68
+ send-pr-comment: true # optional, defaults to true
69
+
70
+ ...
71
+ ```
72
+
73
+ ## Docker
74
+
75
+ Don't use GitHub Actions? You can still use dbt-bouncer via Docker:
76
+
77
+ ```bash
78
+ docker run --rm \
79
+ --volume "$PWD":/app \
80
+ ghcr.io/godatadriven/dbt-bouncer:vX.X.X \
81
+ --config-file /app/<PATH_TO_CONFIG_FILE>
82
+ ```
83
+
84
+ ## Pex
85
+
86
+ You can also run the `.pex` ([Python EXecutable](https://docs.pex-tool.org/whatispex.html#whatispex)) artifact directly once you have a python executable installed:
87
+
88
+ ```bash
89
+ wget https://github.com/godatadriven/dbt-bouncer/releases/download/vX.X.X/dbt-bouncer.pex -O dbt-bouncer.pex
90
+
91
+ dbt-bouncer.pex --config-file $PWD/<PATH_TO_CONFIG_FILE>
92
+ ```
93
+
94
+ ## Python
95
+
96
+ Install from [pypi.org](https://pypi.org/dbt-bouncer):
97
+
98
+ ```shell
99
+ pip install dbt-bouncer
100
+ ```
101
+
102
+ Run:
103
+ ```shell
104
+ dbt-bouncer.pex --config-file $PWD/<PATH_TO_CONFIG_FILE>
105
+ ``
106
+
107
+ # Config file
108
+
109
+ `dbt-bouncer` requires a config file to be provided. This file configures what checks are run. Here is an example config file:
110
+
111
+ ```yaml
112
+ dbt-artifacts-dir: target # [Optional] Directory where the dbt artifacts exists, generally the `target` directory inside a dbt project. Defaults to `./target`.
113
+
114
+ manifest_checks:
115
+ - name: check_macro_name_matches_file_name
116
+ - name: check_model_names
117
+ include: ^staging
118
+ model_name_pattern: ^stg_
119
+ ```
120
+
121
+ # Checks
122
+
123
+ :bulb: Click on a check name to see more details.
124
+
125
+ **Macros**
126
+
127
+ * [`check_macro_arguments_description_populated`](./dbt_bouncer/checks/checks.md#check_macro_arguments_description_populated): Macro arguments must have a populated description.
128
+ * [`check_macro_description_populated`](./dbt_bouncer/checks/checks.md#check_macro_description_populated): Macros must have a populated description.
129
+ * [`check_macro_name_matches_file_name`](./dbt_bouncer/checks/checks.md#check_macro_name_matches_file_name): Macros names must be the same as the file they are contained in.
130
+
131
+ **Metadata**
132
+
133
+ * [`check_project_name`](./dbt_bouncer/checks/checks.md#check_project_name): Enforce that the name of the dbt project matches a supplied regex.
134
+
135
+ **Miscellaneous**
136
+
137
+ * [`check_top_level_directories`](./dbt_bouncer/checks/checks.md#check_top_level_directories): Only specified top-level directories are allowed to contain models.
138
+
139
+ **Models**
140
+
141
+ * [`check_model_description_populated`](./dbt_bouncer/checks/checks.md#check_model_description_populated): Models must have a populated description.
142
+ * [`check_model_names`](./dbt_bouncer/checks/checks.md#check_model_names): Models must have a name that matches the supplied regex.
143
+
144
+ **Sources**
145
+
146
+ * [`check_source_has_meta_keys`](./dbt_bouncer/checks/checks.md#check_source_has_meta_keys): The `meta` config for sources must have the specified keys.
147
+
148
+ # Development
149
+
150
+ To setup your development environment, fork this repository and run:
151
+
152
+ ```bash
153
+ poetry shell
154
+ poetry install
155
+ pre-commit install
156
+ ```
157
+
158
+ Set required environment variables by copying `.env.example` to `.env` and updating the values.
159
+
160
+ All tests can be run via:
161
+ ```bash
162
+ make build-artifacts # Rebuilds dbt artifacts used by pytest
163
+ make test
164
+ ```
@@ -0,0 +1,168 @@
1
+ # `check_macro_arguments_description_populated`
2
+
3
+ Macro arguments must have a populated description.
4
+
5
+ **Argument(s)**:
6
+
7
+ * `include`: (Optional) Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
8
+
9
+ **Example**:
10
+ ```yaml
11
+ checks:
12
+ - name: check_macro_arguments_description_populated
13
+ ```
14
+
15
+ **Required artifact(s)**:
16
+
17
+ * manifest.json
18
+
19
+ ---
20
+
21
+ # `check_macro_description_populated`
22
+
23
+ Macros must have a populated description.
24
+
25
+ **Argument(s)**:
26
+
27
+ * `include`: (Optional) Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
28
+
29
+ **Example**:
30
+ ```yaml
31
+ checks:
32
+ - name: check_macro_description_populated
33
+ ```
34
+
35
+ **Required artifacts(s)**:
36
+
37
+ * manifest.json
38
+
39
+ ---
40
+
41
+ # `check_macro_name_matches_file_name`
42
+
43
+ Macros names must be the same as the file they are contained in.
44
+
45
+ Generic tests are also macros, however to document these tests the "name" value must be precededed with "test_".
46
+
47
+ **Argument(s)**:
48
+
49
+ * `include`: (Optional) Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
50
+
51
+ **Example**:
52
+ ```yaml
53
+ checks:
54
+ - name: check_macro_name_matches_file_name
55
+ ```
56
+
57
+ **Required artifacts(s)**:
58
+
59
+ * manifest.json
60
+
61
+ ---
62
+
63
+ # `check_model_description_populated`
64
+
65
+ Models must have a populated description.
66
+
67
+ **Argument(s)**:
68
+
69
+ * `include`: (Optional) Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
70
+
71
+ **Example**:
72
+ ```yaml
73
+ checks:
74
+ - name: check_model_description_populated
75
+ ```
76
+
77
+ **Required artifacts(s)**:
78
+
79
+ * manifest.json
80
+
81
+ ---
82
+
83
+ # `check_model_names`
84
+
85
+ Models must have a name that matches the supplied regex.
86
+
87
+ **Argument(s)**:
88
+
89
+ * `include`: (Optional) Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
90
+ * `model_name_pattern`: Regex pattern to match the model name.
91
+
92
+ **Example**:
93
+ ```yaml
94
+ checks:
95
+ - name: check_model_names
96
+ include: ^intermediate
97
+ model_name_pattern: ^int_
98
+ - name: check_model_names
99
+ include: ^staging
100
+ model_name_pattern: ^stg_
101
+ ```
102
+
103
+ **Required artifacts(s)**:
104
+
105
+ * manifest.json
106
+
107
+ ---
108
+
109
+ # `check_project_name`
110
+
111
+ Enforce that the name of the dbt project matches a supplied regex. Generally used to enforce that project names conform to something like `company_<DOMAIN>`.
112
+
113
+ **Argument(s)**:
114
+
115
+ * `include`: (Optional) Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
116
+ * `project_name_pattern`: Regex pattern to match the project name.
117
+
118
+ **Example**:
119
+ ```yaml
120
+ checks:
121
+ - name: check_project_name
122
+ ```
123
+
124
+ **Required artifacts(s)**:
125
+
126
+ * manifest.json
127
+
128
+ ---
129
+
130
+ # `check_source_has_meta_keys`
131
+
132
+ The `meta` config for sources must have the specified keys.
133
+
134
+ **Argument(s)**:
135
+
136
+ * `include`: (Optional) Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.
137
+ * `key`:
138
+
139
+ **Example**:
140
+ ```yaml
141
+ checks:
142
+ - name: check_source_has_meta_keys
143
+ keys:
144
+ - contact:
145
+ - email
146
+ - slack
147
+ - owner
148
+ ```
149
+
150
+ **Required artifacts(s)**:
151
+
152
+ * manifest.json
153
+
154
+ ---
155
+
156
+ # `check_top_level_directories`
157
+
158
+ Only specified top-level directories are allowed to contain models.
159
+
160
+ **Example**:
161
+ ```yaml
162
+ checks:
163
+ - name: check_top_level_directories
164
+ ```
165
+
166
+ **Required artifacts(s)**:
167
+
168
+ * manifest.json
@@ -0,0 +1,20 @@
1
+ def pytest_configure(config):
2
+ config.addinivalue_line("markers", "iterate_over_macros: Tests that should run once per macro")
3
+ config.addinivalue_line(
4
+ "markers",
5
+ "iterate_over_models: Tests that should run once per model",
6
+ )
7
+ config.addinivalue_line(
8
+ "markers",
9
+ "iterate_over_sources: Tests that should run once per source",
10
+ )
11
+
12
+ ini_values = {
13
+ "python_classes": ["check_", "Check"],
14
+ "python_files": ["check_*.py"],
15
+ "python_functions": ["check_*"],
16
+ }
17
+
18
+ for name, values in ini_values.items():
19
+ current = config.getini(name)
20
+ current[:] = values
@@ -0,0 +1,47 @@
1
+ import pytest
2
+
3
+ from dbt_bouncer.utils import get_check_inputs
4
+
5
+
6
+ @pytest.mark.iterate_over_macros
7
+ def check_macro_arguments_description_populated(request, check_config=None, macro=None) -> None:
8
+ """
9
+ Macro arguments must have a populated description.
10
+ """
11
+
12
+ macro = get_check_inputs(check_config=check_config, macro=macro, request=request)["macro"]
13
+ for arg in macro["arguments"]:
14
+ assert (
15
+ len(arg["description"].strip()) > 4
16
+ ), f"Argument {arg['name']} in {macro['unique_id']} does not have a populated description."
17
+
18
+
19
+ @pytest.mark.iterate_over_macros
20
+ def check_macro_description_populated(request, check_config=None, macro=None) -> None:
21
+ """
22
+ Macros must have a populated description.
23
+ """
24
+
25
+ macro = get_check_inputs(check_config=check_config, macro=macro, request=request)["macro"]
26
+ assert (
27
+ len(macro["description"].strip()) > 4
28
+ ), f"{macro['unique_id']} does not have a populated description."
29
+
30
+
31
+ @pytest.mark.iterate_over_macros
32
+ def check_macro_name_matches_file_name(request, check_config=None, macro=None) -> None:
33
+ """
34
+ Macros names must be the same as the file they are contained in.
35
+
36
+ Generic tests are also macros, however to document these tests the "name" value must be precededed with "test_".
37
+ """
38
+
39
+ macro = get_check_inputs(macro=macro, request=request)["macro"]
40
+ if macro["name"].startswith("test_"):
41
+ assert (
42
+ macro["name"][5:] == macro["path"].split("/")[-1].split(".")[0]
43
+ ), f"{macro['unique_id']} is not in a file named `{macro['name'][5:]}.sql`."
44
+ else:
45
+ assert (
46
+ macro["name"] == macro["path"].split("/")[-1].split(".")[0]
47
+ ), f"{macro['unique_id']} is not in a file of the same name."
@@ -0,0 +1,18 @@
1
+ import re
2
+
3
+ from dbt_bouncer.utils import get_check_inputs
4
+
5
+
6
+ def check_project_name(manifest_obj, request, check_config=None):
7
+ """
8
+ Enforce that the name of the dbt project matches a supplied regex. Generally used to enforce that project names conform to something like `company_<DOMAIN>`.
9
+ """
10
+
11
+ check_config = get_check_inputs(check_config=check_config, request=request)["check_config"]
12
+
13
+ assert (
14
+ re.compile(check_config["project_name_pattern"].strip()).match(
15
+ manifest_obj.metadata.project_name
16
+ )
17
+ is not None
18
+ ), f"Project name (`{manifest_obj.metadata.project_name}`) does not conform to the supplied regex `({check_config['project_name_pattern'].strip()})`."
@@ -0,0 +1,33 @@
1
+ import re
2
+
3
+ import pytest
4
+
5
+ from dbt_bouncer.utils import get_check_inputs
6
+
7
+
8
+ @pytest.mark.iterate_over_models
9
+ def check_model_description_populated(request, check_config=None, model=None):
10
+ """
11
+ Models must have a populated description.
12
+ """
13
+
14
+ model = get_check_inputs(model=model, request=request)["model"]
15
+
16
+ assert (
17
+ len(model["description"].strip()) > 4
18
+ ), f"{model['unique_id']} does not have a populated description."
19
+
20
+
21
+ @pytest.mark.iterate_over_models
22
+ def check_model_names(request, check_config=None, model=None):
23
+ """
24
+ Models must have a name that matches the supplied regex.
25
+ """
26
+
27
+ input_vars = get_check_inputs(check_config=check_config, model=model, request=request)
28
+ check_config = input_vars["check_config"]
29
+ model = input_vars["model"]
30
+
31
+ assert (
32
+ re.compile(check_config["model_name_pattern"].strip()).match(model["name"]) is not None
33
+ ), f"{model['unique_id']} does not match the supplied regex `({check_config['model_name_pattern'].strip()})`."
@@ -0,0 +1,18 @@
1
+ import pytest
2
+
3
+ from dbt_bouncer.utils import get_check_inputs
4
+
5
+
6
+ @pytest.mark.iterate_over_models
7
+ def check_top_level_directories(request, model=None):
8
+ """
9
+ Only specified top-level directories are allowed to contain models.
10
+ """
11
+
12
+ model = get_check_inputs(model=model, request=request)["model"]
13
+ top_level_dir = model["path"].split("/")[0]
14
+ assert top_level_dir in [
15
+ "staging",
16
+ "intermediate",
17
+ "marts",
18
+ ], f"{model['unique_id']} is located in `{top_level_dir}`, this is not a valid top-level directory."