Skip to content

Commit 6dc05ba

Browse files
authored
Merge pull request #340 from dbt-labs/feature-allow-excluding-models-packages
Feature to allow excluding models and packages
2 parents e3fa5f6 + fd43832 commit 6dc05ba

37 files changed

+188
-13
lines changed

docs/contributing.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Docs are then automatically pushed to the website as part of our CI/CD process.
2424
"markdownlint.config": {
2525
"ul-indent": {"indent": 4},
2626
"MD036": false,
27+
"MD046": false,
2728
}
2829
```
2930

docs/customization/customization.md

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1-
# Disabling Models
1+
# Disabling checks from the package
22

3-
If there is a particular model or set of models that you *do not want this package to execute*, you can
4-
disable these models as you would any other model in your `dbt_project.yml` file
3+
!!! note
4+
5+
This section is describing how to completely deactivate tests from the package.
6+
If you are looking to deactivate models/sources from being tested, you can look at [excluding packages and paths](excluding-packages-and-paths.md)
7+
8+
All the tests done as part of the package are tied to `fct` models.
9+
10+
If there is a particular test or set of tests that you *do not want this package to execute*, you can
11+
disable the corresponding `fct` models as you would any other model in your `dbt_project.yml` file
512

613
``` yaml title="dbt_project.yml"
714
models:
@@ -14,5 +21,4 @@ models:
1421
# disable single DAG model
1522
fct_model_fanout:
1623
+enabled: false
17-
1824
```
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Excluding packages or sources/models based on their path
2+
3+
!!! note
4+
5+
This section is describing how to entirely exclude models/sources and packages to be evaluated.
6+
If you want to document exceptions to the rules, see the section [on exceptions](exceptions.md)
7+
and if you want to deactivate entire tests you can follow instructions from [this page](customization.md)
8+
9+
There might be cases where you want to exclude models/sources from being tested:
10+
11+
- they could come from a package for which you have no control over
12+
- you might be refactoring your project and wanting to exclude entire folders to follow best-practices in the new models
13+
14+
In that case, this package provides the ability to exclude whole packages and/or models and sources based on their path
15+
16+
## Configuration
17+
18+
The variables `exclude_packages` and `exclude_paths_from_project` allow you to define a list of regex patterns to exclude from being reported as errors.
19+
20+
- `exclude_packages` accepts a list of package names to exclude from the tool. To exclude all packages except the current project, you can set it to `["all"]`
21+
- `exclude_paths_from_project` accepts a list of regular expressions of paths to exclude for the current project
22+
- **for models**, the regex provided will try to match the pattern in the string `<path/to/model.sql>`, allowing to exclude packages, but also whole folders or individual models
23+
- **for sources**, the regex will try to match the pattern in `<path/to/sources.yml>:<source_name>.<source_table_name>` *(the pattern is different than for models because the path itself doesn't let us exclude individual sources)*
24+
25+
!!! note
26+
27+
We currently don't allow excluding metrics and exposures, as if those need to be entirely excluded they could be deactivated from the project.
28+
29+
If you have a specific use case requiring this ability, please raise a GitHub issue to explain the situation you'd like to solve and we can revisit this decision !
30+
31+
### Example to exclude a whole package
32+
33+
```yaml title="dbt_project.yml"
34+
vars:
35+
exclude_packages: ["upstream_package"]
36+
```
37+
38+
### Example to exclude models/sources in a given path
39+
40+
```yaml title="dbt_project.yml"
41+
vars:
42+
exclude_paths_from_project: ["/models/legacy/"]
43+
```
44+
45+
### Example to exclude both a package and models/sources in 2 different paths
46+
47+
```yaml title="dbt_project.yml"
48+
vars:
49+
exclude_packages: ["upstream_package"]
50+
exclude_paths_from_project: ["/models/legacy/", "/my_date_spine.sql"]
51+
```
52+
53+
## Tips and tricks
54+
55+
Regular expressions are very powerful but can become complex. After defining your value for `exclude_paths_from_project`, we recommend running the package and inspecting the model `int_all_graph_resources`, checking if the value in the column `is_excluded` matches your expectation.
56+
57+
A useful tool to debug regular expression is [regex101](https://regex101.com/). You can provide a pattern and a list of strings to see which ones actually match the pattern.

integration_tests/dbt_project.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ seeds:
5959

6060
vars:
6161
# ensure integration tests run successfully when there are 0 of a given model type (extra)
62+
exclude_packages: []
63+
exclude_paths_from_project: ["/to_exclude/","source_3.table_6"]
6264
model_types: ['staging', 'intermediate', 'marts', 'other', 'extra', 'new_model_type']
6365
# dummy variable used for testing fct_hard_coded_references
6466
my_table_reference: 'grace_table'
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-- depends on: {{ ref('fct_model_9') }}
2+
3+
select 1 as id
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
-- depends on: {{ ref('fct_model_9') }}
2+
3+
select 1 as id

integration_tests/models/staging/source_1/source.yml

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,4 +17,9 @@ sources:
1717
schema: real_schema_2
1818
# database: real_database
1919
tables:
20-
- name: table_3
20+
- name: table_3
21+
22+
- name: source_3
23+
schema: real_schema_3
24+
tables:
25+
- name: table_6

macros/recursive_dag.sql

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ all_relationships (
2525
parent_file_path,
2626
parent_directory_path,
2727
parent_file_name,
28+
parent_is_excluded,
2829
child_id,
2930
child,
3031
child_resource_type,
@@ -34,6 +35,7 @@ all_relationships (
3435
child_file_path,
3536
child_directory_path,
3637
child_file_name,
38+
child_is_excluded,
3739
distance,
3840
path,
3941
is_dependent_on_chain_of_views
@@ -49,6 +51,7 @@ all_relationships (
4951
file_path as parent_file_path,
5052
directory_path as parent_directory_path,
5153
file_name as parent_file_name,
54+
is_excluded as parent_is_excluded,
5255
resource_id as child_id,
5356
resource_name as child,
5457
resource_type as child_resource_type,
@@ -58,6 +61,7 @@ all_relationships (
5861
file_path as child_file_path,
5962
directory_path as child_directory_path,
6063
file_name as child_file_name,
64+
is_excluded as child_is_excluded,
6165
0 as distance,
6266
{{ dbt.array_construct(['resource_name']) }} as path,
6367
cast(null as boolean) as is_dependent_on_chain_of_views
@@ -78,6 +82,7 @@ all_relationships (
7882
all_relationships.parent_file_path as parent_file_path,
7983
all_relationships.parent_directory_path as parent_directory_path,
8084
all_relationships.parent_file_name as parent_file_name,
85+
all_relationships.parent_is_excluded as parent_is_excluded,
8186
direct_relationships.resource_id as child_id,
8287
direct_relationships.resource_name as child,
8388
direct_relationships.resource_type as child_resource_type,
@@ -87,6 +92,7 @@ all_relationships (
8792
direct_relationships.file_path as child_file_path,
8893
direct_relationships.directory_path as child_directory_path,
8994
direct_relationships.file_name as child_file_name,
95+
direct_relationships.is_excluded as child_is_excluded,
9096
all_relationships.distance+1 as distance,
9197
{{ dbt.array_append('all_relationships.path', 'direct_relationships.resource_name') }} as path,
9298
case
@@ -139,7 +145,8 @@ with direct_relationships as (
139145
resource_id as parent_id,
140146
resource_id as child_id,
141147
resource_name,
142-
materialized as child_materialized
148+
materialized as child_materialized,
149+
is_excluded as child_is_excluded
143150
from direct_relationships
144151
)
145152

@@ -148,6 +155,7 @@ with direct_relationships as (
148155
parent_id,
149156
child_id,
150157
child_materialized,
158+
child_is_excluded,
151159
0 as distance,
152160
{{ dbt.array_construct(['resource_name']) }} as path,
153161
cast(null as boolean) as is_dependent_on_chain_of_views
@@ -161,6 +169,7 @@ with direct_relationships as (
161169
cte_{{i - 1}}.parent_id as parent_id,
162170
direct_relationships.resource_id as child_id,
163171
direct_relationships.materialized as child_materialized,
172+
direct_relationships.is_excluded as child_is_excluded,
164173
cte_{{i - 1}}.distance+1 as distance,
165174
{{ dbt.array_append(prev_cte_path, 'direct_relationships.resource_name') }} as path,
166175
case
@@ -200,6 +209,7 @@ with direct_relationships as (
200209
parent.file_path as parent_file_path,
201210
parent.directory_path as parent_directory_path,
202211
parent.file_name as parent_file_name,
212+
parent.is_excluded as parent_is_excluded,
203213
child.resource_id as child_id,
204214
child.resource_name as child,
205215
child.resource_type as child_resource_type,
@@ -209,6 +219,7 @@ with direct_relationships as (
209219
child.file_path as child_file_path,
210220
child.directory_path as child_directory_path,
211221
child.file_name as child_file_name,
222+
child.is_excluded as child_is_excluded,
212223
all_relationships_unioned.distance,
213224
all_relationships_unioned.path,
214225
all_relationships_unioned.is_dependent_on_chain_of_views

macros/set_is_excluded.sql

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{% macro set_is_excluded(resource, resource_type) %}
2+
{{ return(adapter.dispatch('set_is_excluded', 'dbt_project_evaluator')(resource, resource_type)) }}
3+
{% endmacro %}
4+
5+
{% macro default__set_is_excluded(resource, resource_type) %}
6+
7+
{% set re = modules.re %}
8+
{%- set ns = namespace(exclude=false) -%}
9+
10+
{% if resource_type == 'node' %}
11+
{%- set resource_path = resource.original_file_path | replace("\\","\\\\") -%}
12+
{% elif resource_type == 'source' %}
13+
{%- set resource_path = resource.original_file_path | replace("\\","\\\\") ~ ":" ~ resource.fqn[-2] ~ "." ~ resource.fqn[-1] -%}
14+
{% else %}
15+
{{ exceptions.raise_compiler_error(
16+
"`set_is_excluded()` macro does not support resource type: " ~ resource_type
17+
) }}
18+
{% endif %}
19+
20+
21+
{#- we exclude the resource if it is from the current project and matches the pattern -#}
22+
{%- for exclude_paths_pattern in var('exclude_paths_from_project',[]) -%}
23+
{%- set matched_path = re.search(exclude_paths_pattern, resource_path, re.IGNORECASE) -%}
24+
{%- if matched_path and resource.package_name == project_name %}
25+
{% set ns.exclude = true %}
26+
{%- endif -%}
27+
{%- endfor -%}
28+
29+
{#- we exclude the resource if the package if it is listed in `exclude_packages` or if it is "all" -#}
30+
{%- if (
31+
resource.package_name != project_name)
32+
and (resource.package_name in var('exclude_packages',[]) or 'all' in var('exclude_packages',[]))
33+
-%}
34+
{% set ns.exclude = true %}
35+
{%- endif -%}
36+
37+
{{ return(ns.exclude) }}
38+
39+
{% endmacro %}

macros/unpack/get_node_values.sql

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
{%- for node in nodes_list -%}
1212

1313
{%- set hard_coded_references = dbt_project_evaluator.find_all_hard_coded_references(node) -%}
14+
{%- set exclude_node = dbt_project_evaluator.set_is_excluded(node, resource_type="node") -%}
15+
1416

1517
{%- set values_line =
1618
[
@@ -30,7 +32,8 @@
3032
wrap_string_with_quotes(node.meta | tojson),
3133
wrap_string_with_quotes(dbt.escape_single_quotes(hard_coded_references)),
3234
wrap_string_with_quotes(node.get('depends_on',{}).get('macros',[]) | tojson),
33-
"cast(" ~ dbt_project_evaluator.is_not_empty_string(node.test_metadata) | trim ~ " as boolean)"
35+
"cast(" ~ dbt_project_evaluator.is_not_empty_string(node.test_metadata) | trim ~ " as boolean)",
36+
"cast(" ~ exclude_node ~ " as boolean)",
3437
]
3538
%}
3639

0 commit comments

Comments
 (0)