diff --git a/latest/checks/checks_catalog/index.html b/latest/checks/checks_catalog/index.html index 5498d5a9..5a074a75 100644 --- a/latest/checks/checks_catalog/index.html +++ b/latest/checks/checks_catalog/index.html @@ -933,7 +933,7 @@

manifest_checks:
     - name: check_column_description_populated
-      include: ^marts
+      include: ^models/marts
 

@@ -994,7 +994,7 @@

```yaml manifest_checks: - name: check_column_description_populated - include: ^marts + include: ^models/marts ``` """ diff --git a/latest/checks/checks_manifest/index.html b/latest/checks/checks_manifest/index.html index 38c8e4eb..feb84e16 100644 --- a/latest/checks/checks_manifest/index.html +++ b/latest/checks/checks_manifest/index.html @@ -1119,14 +1119,14 @@

manifest_checks:
     - name: check_lineage_permitted_upstream_models
-      include: ^staging
+      include: ^models/staging
       upstream_path_pattern: $^
     - name: check_lineage_permitted_upstream_models
-      include: ^intermediate
-      upstream_path_pattern: ^staging|^intermediate
+      include: ^models/intermediate
+      upstream_path_pattern: ^models/staging|^models/intermediate
     - name: check_lineage_permitted_upstream_models
-      include: ^marts
-      upstream_path_pattern: ^staging|^intermediate
+      include: ^models/marts
+      upstream_path_pattern: ^models/staging|^models/intermediate
 

@@ -1204,14 +1204,14 @@

```yaml manifest_checks: - name: check_lineage_permitted_upstream_models - include: ^staging + include: ^models/staging upstream_path_pattern: $^ - name: check_lineage_permitted_upstream_models - include: ^intermediate - upstream_path_pattern: ^staging|^intermediate + include: ^models/intermediate + upstream_path_pattern: ^models/staging|^models/intermediate - name: check_lineage_permitted_upstream_models - include: ^marts - upstream_path_pattern: ^staging|^intermediate + include: ^models/marts + upstream_path_pattern: ^models/staging|^models/intermediate ``` """ @@ -1225,7 +1225,7 @@

upstream_model for upstream_model in upstream_models if re.compile(upstream_path_pattern.strip()).match( - [m for m in models if m.unique_id == upstream_model][0].path + [m for m in models if m.unique_id == upstream_model][0].original_file_path ) is None ] @@ -1296,7 +1296,7 @@

manifest_checks:
     - name: check_lineage_seed_cannot_be_used
-      include: ^intermediate|^marts
+      include: ^models/intermediate|^models/marts
 

@@ -1341,7 +1341,7 @@

```yaml manifest_checks: - name: check_lineage_seed_cannot_be_used - include: ^intermediate|^marts + include: ^models/intermediate|^models/marts ``` """ @@ -1412,7 +1412,7 @@

manifest_checks:
     - name: check_lineage_source_cannot_be_used
-      include: ^intermediate|^marts
+      include: ^models/intermediate|^models/marts
 

@@ -1457,7 +1457,7 @@

```yaml manifest_checks: - name: check_lineage_source_cannot_be_used - include: ^intermediate|^marts + include: ^models/intermediate|^models/marts ``` """ @@ -2210,11 +2210,11 @@

if macro.name.startswith("test_"): assert ( - macro.name[5:] == macro.path.split("/")[-1].split(".")[0] + macro.name[5:] == macro.original_file_path.split("/")[-1].split(".")[0] ), f"Macro `{macro.unique_id}` is not in a file named `{macro.name[5:]}.sql`." else: assert ( - macro.name == macro.path.split("/")[-1].split(".")[0] + macro.name == macro.original_file_path.split("/")[-1].split(".")[0] ), f"Macro `{macro.name}` is not in a file of the same name."

@@ -2322,7 +2322,9 @@

272 273 274 -275
@pytest.mark.iterate_over_macros
+275
+276
+277
@pytest.mark.iterate_over_macros
 @bouncer_check
 def check_macro_property_file_location(
     request: TopRequest, macro: Union[Macros, None] = None, **kwargs
@@ -2342,25 +2344,27 @@ 

``` """ - expected_substr = "_".join(macro.path[6:].split("/")[:-1]) + expected_substr = "_".join(macro.original_file_path[6:].split("/")[:-1]) properties_yml_name = macro.patch_path.split("/")[-1] - if macro.path.startswith("tests/"): # Do not check generic tests (which are also macros) - pass - elif expected_substr == "": # i.e. macro in ./macros - assert ( - properties_yml_name == "_macros.yml" - ), f"The properties file for `{macro.name}` (`{properties_yml_name}`) should be `_macros.yml`." - else: - assert properties_yml_name.startswith( - "_" - ), f"The properties file for `{macro.name}` (`{properties_yml_name}`) does not start with an underscore." - assert ( - expected_substr in properties_yml_name - ), f"The properties file for `{macro.name}` (`{properties_yml_name}`) does not contain the expected substring (`{expected_substr}`)." - assert properties_yml_name.endswith( - "__macros.yml" - ), f"The properties file for `{macro.name.name}` (`{properties_yml_name}`) does not end with `__macros.yml`." + if macro.original_file_path.startswith( + "tests/" + ): # Do not check generic tests (which are also macros) + pass + elif expected_substr == "": # i.e. macro in ./macros + assert ( + properties_yml_name == "_macros.yml" + ), f"The properties file for `{macro.name}` (`{properties_yml_name}`) should be `_macros.yml`." + else: + assert properties_yml_name.startswith( + "_" + ), f"The properties file for `{macro.name}` (`{properties_yml_name}`) does not start with an underscore." + assert ( + expected_substr in properties_yml_name + ), f"The properties file for `{macro.name}` (`{properties_yml_name}`) does not contain the expected substring (`{expected_substr}`)." + assert properties_yml_name.endswith( + "__macros.yml" + ), f"The properties file for `{macro.name.name}` (`{properties_yml_name}`) does not end with `__macros.yml`."

@@ -2599,13 +2603,13 @@

# Align with dbt best practices that marts should be `public`, everything else should be `protected` - name: check_model_access access: protected - include: ^intermediate + include: ^models/intermediate - name: check_model_access access: public - include: ^marts + include: ^models/marts - name: check_model_access access: protected - include: ^staging + include: ^models/staging

@@ -2668,13 +2672,13 @@

# Align with dbt best practices that marts should be `public`, everything else should be `protected` - name: check_model_access access: protected - include: ^intermediate + include: ^models/intermediate - name: check_model_access access: public - include: ^marts + include: ^models/marts - name: check_model_access access: protected - include: ^staging + include: ^models/staging ``` """ @@ -3136,8 +3140,8 @@

``` """ - model_doc_dir = model.patch_path[model.patch_path.find("models") :].split("/")[1:-1] - model_sql_dir = model.path.split("/")[:-1] + model_doc_dir = model.patch_path[model.patch_path.find("models") :].split("/")[:-1] + model_sql_dir = model.original_file_path.split("/")[:-1] assert ( model_doc_dir == model_sql_dir @@ -3463,18 +3467,17 @@

Example(s):

manifest_checks:
-# Special case for top level directories within `./models`, pass "" to `include`
-- name: check_model_directories
-    include: ""
-    permitted_sub_directories:
-    - intermediate
-    - marts
-    - staging
+- name: check_model_directories
+  include: models
+  permitted_sub_directories:
+    - intermediate
+    - marts
+    - staging
 
# Restrict sub-directories within `./models/staging`
 - name: check_model_directories
-    include: ^staging
-    permitted_sub_directories:
+  include: ^models/staging
+  permitted_sub_directories:
     - crm
     - payments
 

@@ -3524,14 +3527,7 @@

319 320 321 -322 -323 -324 -325 -326 -327 -328 -329
@pytest.mark.iterate_over_models
+322
@pytest.mark.iterate_over_models
 @bouncer_check
 def check_model_directories(
     request: TopRequest,
@@ -3552,36 +3548,29 @@ 

Example(s): ```yaml manifest_checks: - # Special case for top level directories within `./models`, pass "" to `include` - - name: check_model_directories - include: "" - permitted_sub_directories: - - intermediate - - marts - - staging - ``` - ```yaml - # Restrict sub-directories within `./models/staging` - - name: check_model_directories - include: ^staging - permitted_sub_directories: - - crm - - payments - ``` - """ - - # Special case for `models` directory - if include == "": - assert ( - model.path.split("/")[0] in permitted_sub_directories # type: ignore[operator] - ), f"`{model.name}` is located in `{model.path.split('/')[0]}`, this is not a valid sub-directory." - else: - matched_path = re.compile(include.strip()).match(model.path) - path_after_match = model.path[matched_path.end() + 1 :] # type: ignore[union-attr] - - assert ( - path_after_match.split("/")[0] in permitted_sub_directories # type: ignore[operator] - ), f"`{model.name}` is located in `{model.path.split('/')[0]}`, this is not a valid sub-directory." + - name: check_model_directories + include: models + permitted_sub_directories: + - intermediate + - marts + - staging + ``` + ```yaml + # Restrict sub-directories within `./models/staging` + - name: check_model_directories + include: ^models/staging + permitted_sub_directories: + - crm + - payments + ``` + """ + + matched_path = re.compile(include.strip()).match(model.original_file_path) + path_after_match = model.original_file_path[matched_path.end() + 1 :] # type: ignore[union-attr] + + assert ( + path_after_match.split("/")[0] in permitted_sub_directories # type: ignore[operator] + ), f"`{model.name}` is located in `{model.original_file_path.split('/')[1]}`, this is not a valid sub-directory. {path_after_match.split('/')[0]}"

@@ -3646,12 +3635,19 @@

manifest_checks:
     - name: check_model_has_contracts_enforced
-        include: ^marts
+      include: ^models/marts
 

Source code in src/dbt_bouncer/checks/manifest/check_models.py -
336
+              
329
+330
+331
+332
+333
+334
+335
+336
 337
 338
 339
@@ -3665,35 +3661,28 @@ 

347 348 349 -350 -351 -352 -353 -354 -355 -356 -357

@pytest.mark.iterate_over_models
-@bouncer_check
-def check_model_has_contracts_enforced(
-    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs
-) -> None:
-    """
-    Model must have contracts enforced.
-
-    Receives:
-        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
-        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
-        model (DbtBouncerModel): The DbtBouncerModel object to check.
-
-    Example(s):
-        ```yaml
-        manifest_checks:
-            - name: check_model_has_contracts_enforced
-                include: ^marts
-        ```
-    """
-
-    assert model.contract.enforced is True, f"`{model.name}` does not have contracts enforced."
+350
@pytest.mark.iterate_over_models
+@bouncer_check
+def check_model_has_contracts_enforced(
+    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs
+) -> None:
+    """
+    Model must have contracts enforced.
+
+    Receives:
+        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
+        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
+        model (DbtBouncerModel): The DbtBouncerModel object to check.
+
+    Example(s):
+        ```yaml
+        manifest_checks:
+            - name: check_model_has_contracts_enforced
+              include: ^models/marts
+        ```
+    """
+
+    assert model.contract.enforced is True, f"`{model.name}` does not have contracts enforced."
 
@@ -3775,7 +3764,14 @@

Source code in src/dbt_bouncer/checks/manifest/check_models.py -
365
+              
358
+359
+360
+361
+362
+363
+364
+365
 366
 367
 368
@@ -3801,47 +3797,40 @@ 

388 389 390 -391 -392 -393 -394 -395 -396 -397 -398

@pytest.mark.iterate_over_models
-@bouncer_check
-def check_model_has_meta_keys(
-    request: TopRequest,
-    keys: Union[NestedDict, None] = None,
-    model: Union[DbtBouncerModel, None] = None,
-    **kwargs,
-) -> None:
-    """
-    The `meta` config for models must have the specified keys.
-
-    Receives:
-        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
-        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
-        keys (NestedDict): A list (that may contain sub-lists) of required keys.
-        model (DbtBouncerModel): The DbtBouncerModel object to check.
-
-    Example(s):
-        ```yaml
-        manifest_checks:
-            - name: check_model_has_meta_keys
-              keys:
-                - maturity
-                - owner
-        ```
-    """
-
-    missing_keys = find_missing_meta_keys(
-        meta_config=model.meta,
-        required_keys=keys.model_dump(),
-    )
-    assert (
-        missing_keys == []
-    ), f"`{model.name}` is missing the following keys from the `meta` config: {[x.replace('>>', '') for x in missing_keys]}"
+391
@pytest.mark.iterate_over_models
+@bouncer_check
+def check_model_has_meta_keys(
+    request: TopRequest,
+    keys: Union[NestedDict, None] = None,
+    model: Union[DbtBouncerModel, None] = None,
+    **kwargs,
+) -> None:
+    """
+    The `meta` config for models must have the specified keys.
+
+    Receives:
+        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
+        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
+        keys (NestedDict): A list (that may contain sub-lists) of required keys.
+        model (DbtBouncerModel): The DbtBouncerModel object to check.
+
+    Example(s):
+        ```yaml
+        manifest_checks:
+            - name: check_model_has_meta_keys
+              keys:
+                - maturity
+                - owner
+        ```
+    """
+
+    missing_keys = find_missing_meta_keys(
+        meta_config=model.meta,
+        required_keys=keys.model_dump(),
+    )
+    assert (
+        missing_keys == []
+    ), f"`{model.name}` is missing the following keys from the `meta` config: {[x.replace('>>', '') for x in missing_keys]}"
 
@@ -3910,7 +3899,14 @@

Source code in src/dbt_bouncer/checks/manifest/check_models.py -
405
+              
398
+399
+400
+401
+402
+403
+404
+405
 406
 407
 408
@@ -3925,36 +3921,29 @@ 

417 418 419 -420 -421 -422 -423 -424 -425 -426 -427

@pytest.mark.iterate_over_models
-@bouncer_check
-def check_model_has_no_upstream_dependencies(
-    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs
-) -> None:
-    """
-    Identify if models have no upstream dependencies as this likely indicates hard-coded tables references.
-
-    Receives:
-        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
-        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
-        model (DbtBouncerModel): The DbtBouncerModel object to check.
+420
@pytest.mark.iterate_over_models
+@bouncer_check
+def check_model_has_no_upstream_dependencies(
+    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs
+) -> None:
+    """
+    Identify if models have no upstream dependencies as this likely indicates hard-coded tables references.
+
+    Receives:
+        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
+        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
+        model (DbtBouncerModel): The DbtBouncerModel object to check.
+
+    Example(s):
+        ```yaml
+        manifest_checks:
+            - name: check_model_has_no_upstream_dependencies
+        ```
+    """
 
-    Example(s):
-        ```yaml
-        manifest_checks:
-            - name: check_model_has_no_upstream_dependencies
-        ```
-    """
-
-    assert (
-        len(model.depends_on.nodes) > 0
-    ), f"`{model.name}` has no upstream dependencies, this likely indicates hard-coded tables references."
+    assert (
+        len(model.depends_on.nodes) > 0
+    ), f"`{model.name}` has no upstream dependencies, this likely indicates hard-coded tables references."
 
@@ -4036,7 +4025,14 @@

Source code in src/dbt_bouncer/checks/manifest/check_models.py -
435
+              
428
+429
+430
+431
+432
+433
+434
+435
 436
 437
 438
@@ -4057,42 +4053,35 @@ 

453 454 455 -456 -457 -458 -459 -460 -461 -462 -463

@pytest.mark.iterate_over_models
-@bouncer_check
-def check_model_has_tags(
-    request: TopRequest,
-    model: Union[DbtBouncerModel, None] = None,
-    tags: Union[List[str], None] = None,
-    **kwargs,
-) -> None:
-    """
-    Models must have the specified tags.
-
-    Receives:
-        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
-        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
-        model (DbtBouncerModel): The DbtBouncerModel object to check.
-        tags (List[str]): List of tags to check for.
-
-    Example(s):
-        ```yaml
-        manifest_checks:
-            - name: check_model_has_tags
-              tags:
-                - tag_1
-                - tag_2
-        ```
-    """
-
-    missing_tags = [tag for tag in tags if tag not in model.tags]
-    assert not missing_tags, f"`{model.name}` is missing required tags: {missing_tags}."
+456
@pytest.mark.iterate_over_models
+@bouncer_check
+def check_model_has_tags(
+    request: TopRequest,
+    model: Union[DbtBouncerModel, None] = None,
+    tags: Union[List[str], None] = None,
+    **kwargs,
+) -> None:
+    """
+    Models must have the specified tags.
+
+    Receives:
+        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
+        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
+        model (DbtBouncerModel): The DbtBouncerModel object to check.
+        tags (List[str]): List of tags to check for.
+
+    Example(s):
+        ```yaml
+        manifest_checks:
+            - name: check_model_has_tags
+              tags:
+                - tag_1
+                - tag_2
+        ```
+    """
+
+    missing_tags = [tag for tag in tags if tag not in model.tags]
+    assert not missing_tags, f"`{model.name}` is missing required tags: {missing_tags}."
 
@@ -4167,7 +4156,7 @@

manifest_checks:
   - name: check_model_has_unique_test
-    include: ^fct_|^dim_
+    include: ^models/marts
 
manifest_checks:
   # Example of allowing a custom uniqueness test
@@ -4180,7 +4169,14 @@ 

Source code in src/dbt_bouncer/checks/manifest/check_models.py -
477
+              
470
+471
+472
+473
+474
+475
+476
+477
 478
 479
 480
@@ -4215,56 +4211,49 @@ 

509 510 511 -512 -513 -514 -515 -516 -517 -518 -519

@pytest.mark.iterate_over_models
-@bouncer_check
-def check_model_has_unique_test(
-    request: TopRequest,
-    tests: List[DbtBouncerModel],
-    accepted_uniqueness_tests: Union[List[str], None] = None,
-    model: Union[DbtBouncerModel, None] = None,
-    **kwargs,
-) -> None:
-    """
-    Models must have a test for uniqueness of a column.
-
-    Receives:
-        accepted_uniqueness_tests (Optional[List[str]]): List of tests that are accepted as uniqueness tests. If not provided, defaults to `expect_compound_columns_to_be_unique`, `dbt_utils.unique_combination_of_columns` and `unique`.
-        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
-        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
-        model (DbtBouncerModel): The DbtBouncerModel object to check.
-
-    Example(s):
-        ```yaml
-        manifest_checks:
-          - name: check_model_has_unique_test
-            include: ^fct_|^dim_
-        ```
-        ```yaml
-        manifest_checks:
-          # Example of allowing a custom uniqueness test
-          - name: check_model_has_unique_test
-            accepted_uniqueness_tests:
-                - expect_compound_columns_to_be_unique
-                - my_custom_uniqueness_test
-                - unique
-        ```
-    """
-
-    num_unique_tests = sum(
-        test.attached_node == model.unique_id
-        and test.test_metadata.name in accepted_uniqueness_tests  # type: ignore[operator]
-        for test in tests
-    )
-    assert (
-        num_unique_tests >= 1
-    ), f"`{model.name}` does not have a test for uniqueness of a column."
+512
@pytest.mark.iterate_over_models
+@bouncer_check
+def check_model_has_unique_test(
+    request: TopRequest,
+    tests: List[DbtBouncerModel],
+    accepted_uniqueness_tests: Union[List[str], None] = None,
+    model: Union[DbtBouncerModel, None] = None,
+    **kwargs,
+) -> None:
+    """
+    Models must have a test for uniqueness of a column.
+
+    Receives:
+        accepted_uniqueness_tests (Optional[List[str]]): List of tests that are accepted as uniqueness tests. If not provided, defaults to `expect_compound_columns_to_be_unique`, `dbt_utils.unique_combination_of_columns` and `unique`.
+        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
+        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
+        model (DbtBouncerModel): The DbtBouncerModel object to check.
+
+    Example(s):
+        ```yaml
+        manifest_checks:
+          - name: check_model_has_unique_test
+            include: ^models/marts
+        ```
+        ```yaml
+        manifest_checks:
+          # Example of allowing a custom uniqueness test
+          - name: check_model_has_unique_test
+            accepted_uniqueness_tests:
+                - expect_compound_columns_to_be_unique
+                - my_custom_uniqueness_test
+                - unique
+        ```
+    """
+
+    num_unique_tests = sum(
+        test.attached_node == model.unique_id
+        and test.test_metadata.name in accepted_uniqueness_tests  # type: ignore[operator]
+        for test in tests
+    )
+    assert (
+        num_unique_tests >= 1
+    ), f"`{model.name}` does not have a test for uniqueness of a column."
 
@@ -4343,7 +4332,7 @@

manifest_checks:
     - name: check_model_has_unit_tests
-      include: ^marts
+      include: ^models/marts
 
manifest_checks:
     - name: check_model_has_unit_tests
@@ -4352,7 +4341,14 @@ 

Source code in src/dbt_bouncer/checks/manifest/check_models.py -
527
+              
520
+521
+522
+523
+524
+525
+526
+527
 528
 529
 530
@@ -4391,60 +4387,53 @@ 

563 564 565 -566 -567 -568 -569 -570 -571 -572 -573

@pytest.mark.iterate_over_models
-@bouncer_check
-def check_model_has_unit_tests(
-    manifest_obj: DbtBouncerManifest,
-    request: TopRequest,
-    unit_tests: List[UnitTests],
-    min_number_of_unit_tests: Union[int, None] = None,
-    model: Union[DbtBouncerModel, None] = None,
-    **kwargs,
-) -> None:
-    """
-    Models must have more than the specified number of unit tests.
-
-    Receives:
-        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
-        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
-        min_number_of_unit_tests (Optional[int]): The minimum number of unit tests that a model must have. Default: 1.
-        model (DbtBouncerModel): The DbtBouncerModel object to check.
-
-    !!! warning
-
-        This check is only supported for dbt 1.8.0 and above.
-
-    Example(s):
-        ```yaml
-        manifest_checks:
-            - name: check_model_has_unit_tests
-              include: ^marts
-        ```
-        ```yaml
-        manifest_checks:
-            - name: check_model_has_unit_tests
-              min_number_of_unit_tests: 2
-        ```
-    """
-
-    if semver.Version.parse(manifest_obj.manifest.metadata.dbt_version) >= "1.8.0":
-        num_unit_tests = len(
-            [t.unique_id for t in unit_tests if t.depends_on.nodes[0] == model.unique_id]
+566
@pytest.mark.iterate_over_models
+@bouncer_check
+def check_model_has_unit_tests(
+    manifest_obj: DbtBouncerManifest,
+    request: TopRequest,
+    unit_tests: List[UnitTests],
+    min_number_of_unit_tests: Union[int, None] = None,
+    model: Union[DbtBouncerModel, None] = None,
+    **kwargs,
+) -> None:
+    """
+    Models must have more than the specified number of unit tests.
+
+    Receives:
+        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
+        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
+        min_number_of_unit_tests (Optional[int]): The minimum number of unit tests that a model must have. Default: 1.
+        model (DbtBouncerModel): The DbtBouncerModel object to check.
+
+    !!! warning
+
+        This check is only supported for dbt 1.8.0 and above.
+
+    Example(s):
+        ```yaml
+        manifest_checks:
+            - name: check_model_has_unit_tests
+              include: ^models/marts
+        ```
+        ```yaml
+        manifest_checks:
+            - name: check_model_has_unit_tests
+              min_number_of_unit_tests: 2
+        ```
+    """
+
+    if semver.Version.parse(manifest_obj.manifest.metadata.dbt_version) >= "1.8.0":
+        num_unit_tests = len(
+            [t.unique_id for t in unit_tests if t.depends_on.nodes[0] == model.unique_id]
+        )
+        assert (
+            num_unit_tests >= min_number_of_unit_tests  # type: ignore[operator]
+        ), f"`{model.name}` has {num_unit_tests} unit tests, this is less than the minimum of {min_number_of_unit_tests}."
+    else:
+        logging.warning(
+            "The `check_model_has_unit_tests` check is only supported for dbt 1.8.0 and above."
         )
-        assert (
-            num_unit_tests >= min_number_of_unit_tests  # type: ignore[operator]
-        ), f"`{model.name}` has {num_unit_tests} unit tests, this is less than the minimum of {min_number_of_unit_tests}."
-    else:
-        logging.warning(
-            "The `check_model_has_unit_tests` check is only supported for dbt 1.8.0 and above."
-        )
 
@@ -4541,7 +4530,14 @@

Source code in src/dbt_bouncer/checks/manifest/check_models.py -
586
+              
579
+580
+581
+582
+583
+584
+585
+586
 587
 588
 589
@@ -4624,104 +4620,97 @@ 

666 667 668 -669 -670 -671 -672 -673 -674 -675 -676

@pytest.mark.iterate_over_models
-@bouncer_check
-def check_model_max_chained_views(
-    manifest_obj: DbtBouncerManifest,
-    models: List[DbtBouncerModel],
-    request: TopRequest,
-    materializations_to_include: Union[List[str], None] = None,
-    max_chained_views: Union[int, None] = None,
-    model: Union[DbtBouncerModel, None] = None,
-    **kwargs,
-) -> None:
-    """
-    Models cannot have more than the specified number of upstream dependents that are not tables (default: 3).
+669
@pytest.mark.iterate_over_models
+@bouncer_check
+def check_model_max_chained_views(
+    manifest_obj: DbtBouncerManifest,
+    models: List[DbtBouncerModel],
+    request: TopRequest,
+    materializations_to_include: Union[List[str], None] = None,
+    max_chained_views: Union[int, None] = None,
+    model: Union[DbtBouncerModel, None] = None,
+    **kwargs,
+) -> None:
+    """
+    Models cannot have more than the specified number of upstream dependents that are not tables (default: 3).
+
+    Receives:
+        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
+        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
+        materializations_to_include (Optional[List[str]]): List of materializations to include in the check. If not provided, defaults to `ephemeral` and `view`.
+        max_chained_views (Optional[int]): The maximum number of upstream dependents that are not tables. Default: 3
+        model (DbtBouncerModel): The DbtBouncerModel object to check.
 
-    Receives:
-        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
-        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
-        materializations_to_include (Optional[List[str]]): List of materializations to include in the check. If not provided, defaults to `ephemeral` and `view`.
-        max_chained_views (Optional[int]): The maximum number of upstream dependents that are not tables. Default: 3
-        model (DbtBouncerModel): The DbtBouncerModel object to check.
-
-    Example(s):
-        ```yaml
-        manifest_checks:
-            - name: check_model_max_chained_views
-        ```
-        ```yaml
-        manifest_checks:
-            - name: check_model_max_chained_views
-              materializations_to_include:
-                - ephemeral
-                - my_custom_materialization
-                - view
-              max_chained_views: 5
-        ```
-    """
-
-    def return_upstream_view_models(
-        materializations,
-        max_chained_views,
-        models,
-        model_unique_ids_to_check,
-        package_name,
-        depth=0,
-    ):
-        """
-        Recursive function to return model unique_id's of upstream models that are views. Depth of recursion can be specified. If no models meet the criteria then an empty list is returned.
-        """
-
-        if depth == max_chained_views or model_unique_ids_to_check == []:
-            return model_unique_ids_to_check
-
-        relevant_upstream_models = []
-        for model in model_unique_ids_to_check:
-            upstream_nodes = list(
-                [m2 for m2 in models if m2.unique_id == model][0].depends_on.nodes
-            )
-            if upstream_nodes != []:
-                upstream_models = [
-                    m
-                    for m in upstream_nodes
-                    if m.split(".")[0] == "model" and m.split(".")[1] == package_name
-                ]
-                for i in upstream_models:
-                    if [m for m in models if m.unique_id == i][
-                        0
-                    ].config.materialized in materializations:
-                        relevant_upstream_models.append(i)
-
-        depth += 1
-        return return_upstream_view_models(
-            materializations=materializations,
-            max_chained_views=max_chained_views,
-            models=models,
-            model_unique_ids_to_check=relevant_upstream_models,
-            package_name=package_name,
-            depth=depth,
-        )
-
-    assert (
-        len(
-            return_upstream_view_models(
-                materializations=materializations_to_include,
-                max_chained_views=max_chained_views,
-                models=models,
-                model_unique_ids_to_check=[model.unique_id],
-                package_name=manifest_obj.manifest.metadata.project_name,
-            )
-        )
-        == 0
-    ), f"`{model.name}` has more than {max_chained_views} upstream dependents that are not tables."
+    Example(s):
+        ```yaml
+        manifest_checks:
+            - name: check_model_max_chained_views
+        ```
+        ```yaml
+        manifest_checks:
+            - name: check_model_max_chained_views
+              materializations_to_include:
+                - ephemeral
+                - my_custom_materialization
+                - view
+              max_chained_views: 5
+        ```
+    """
+
+    def return_upstream_view_models(
+        materializations,
+        max_chained_views,
+        models,
+        model_unique_ids_to_check,
+        package_name,
+        depth=0,
+    ):
+        """
+        Recursive function to return model unique_id's of upstream models that are views. Depth of recursion can be specified. If no models meet the criteria then an empty list is returned.
+        """
+
+        if depth == max_chained_views or model_unique_ids_to_check == []:
+            return model_unique_ids_to_check
+
+        relevant_upstream_models = []
+        for model in model_unique_ids_to_check:
+            upstream_nodes = list(
+                [m2 for m2 in models if m2.unique_id == model][0].depends_on.nodes
+            )
+            if upstream_nodes != []:
+                upstream_models = [
+                    m
+                    for m in upstream_nodes
+                    if m.split(".")[0] == "model" and m.split(".")[1] == package_name
+                ]
+                for i in upstream_models:
+                    if [m for m in models if m.unique_id == i][
+                        0
+                    ].config.materialized in materializations:
+                        relevant_upstream_models.append(i)
+
+        depth += 1
+        return return_upstream_view_models(
+            materializations=materializations,
+            max_chained_views=max_chained_views,
+            models=models,
+            model_unique_ids_to_check=relevant_upstream_models,
+            package_name=package_name,
+            depth=depth,
+        )
+
+    assert (
+        len(
+            return_upstream_view_models(
+                materializations=materializations_to_include,
+                max_chained_views=max_chained_views,
+                models=models,
+                model_unique_ids_to_check=[model.unique_id],
+                package_name=manifest_obj.manifest.metadata.project_name,
+            )
+        )
+        == 0
+    ), f"`{model.name}` has more than {max_chained_views} upstream dependents that are not tables."
 
@@ -4801,7 +4790,14 @@

Source code in src/dbt_bouncer/checks/manifest/check_models.py -
684
+              
677
+678
+679
+680
+681
+682
+683
+684
 685
 686
 687
@@ -4824,44 +4820,37 @@ 

704 705 706 -707 -708 -709 -710 -711 -712 -713 -714

@pytest.mark.iterate_over_models
-@bouncer_check
-def check_model_max_fanout(
-    models: List[DbtBouncerModel],
-    request: TopRequest,
-    max_downstream_models: Union[int, None] = None,
-    model: Union[DbtBouncerModel, None] = None,
-    **kwargs,
-) -> None:
-    """
-    Models cannot have more than the specified number of downstream models (default: 3).
-
-    Receives:
-        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
-        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
-        max_downstream_models (Optional[int]): The maximum number of permitted downstream models. Default: 3
-        model (DbtBouncerModel): The DbtBouncerModel object to check.
-
-    Example(s):
-        ```yaml
-        manifest_checks:
-            - name: check_model_max_fanout
-              max_downstream_models: 2
-        ```
-    """
-
-    num_downstream_models = sum(model.unique_id in m.depends_on.nodes for m in models)
-
-    assert (
-        num_downstream_models <= max_downstream_models  # type: ignore[operator]
-    ), f"`{model.name}` has {num_downstream_models} downstream models, which is more than the permitted maximum of {max_downstream_models}."
+707
@pytest.mark.iterate_over_models
+@bouncer_check
+def check_model_max_fanout(
+    models: List[DbtBouncerModel],
+    request: TopRequest,
+    max_downstream_models: Union[int, None] = None,
+    model: Union[DbtBouncerModel, None] = None,
+    **kwargs,
+) -> None:
+    """
+    Models cannot have more than the specified number of downstream models (default: 3).
+
+    Receives:
+        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
+        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
+        max_downstream_models (Optional[int]): The maximum number of permitted downstream models. Default: 3
+        model (DbtBouncerModel): The DbtBouncerModel object to check.
+
+    Example(s):
+        ```yaml
+        manifest_checks:
+            - name: check_model_max_fanout
+              max_downstream_models: 2
+        ```
+    """
+
+    num_downstream_models = sum(model.unique_id in m.depends_on.nodes for m in models)
+
+    assert (
+        num_downstream_models <= max_downstream_models  # type: ignore[operator]
+    ), f"`{model.name}` has {num_downstream_models} downstream models, which is more than the permitted maximum of {max_downstream_models}."
 
@@ -4954,7 +4943,14 @@

Source code in src/dbt_bouncer/checks/manifest/check_models.py -
722
+              
715
+716
+717
+718
+719
+720
+721
+722
 723
 724
 725
@@ -4981,48 +4977,41 @@ 

746 747 748 -749 -750 -751 -752 -753 -754 -755 -756

@pytest.mark.iterate_over_models
-@bouncer_check
-def check_model_max_number_of_lines(
-    request: TopRequest,
-    max_number_of_lines: Union[int, None] = None,
-    model: Union[DbtBouncerModel, None] = None,
-    **kwargs,
-) -> None:
-    """
-    Models may not have more than the specified number of lines.
+749
@pytest.mark.iterate_over_models
+@bouncer_check
+def check_model_max_number_of_lines(
+    request: TopRequest,
+    max_number_of_lines: Union[int, None] = None,
+    model: Union[DbtBouncerModel, None] = None,
+    **kwargs,
+) -> None:
+    """
+    Models may not have more than the specified number of lines.
+
+    Receives:
+        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
+        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern
+        will be checked.
+        max_number_of_lines (int): The maximum number of permitted lines. Default: 100.
+        model (DbtBouncerModel): The DbtBouncerModel object to check.
 
-    Receives:
-        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
-        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern
-        will be checked.
-        max_number_of_lines (int): The maximum number of permitted lines. Default: 100.
-        model (DbtBouncerModel): The DbtBouncerModel object to check.
-
-    Example(s):
-        ```yaml
-        manifest_checks:
-            - name: check_model_max_number_of_lines
-        ```
-        ```yaml
-        manifest_checks:
-            - name: check_model_max_number_of_lines
-              max_number_of_lines: 150
-        ```
-    """
-
-    actual_number_of_lines = model.raw_code.count("\n") + 1
-
-    assert (
-        actual_number_of_lines <= max_number_of_lines
-    ), f"`{model.name}` has {actual_number_of_lines} lines, this is more than the maximum permitted number of lines ({max_number_of_lines})."
+    Example(s):
+        ```yaml
+        manifest_checks:
+            - name: check_model_max_number_of_lines
+        ```
+        ```yaml
+        manifest_checks:
+            - name: check_model_max_number_of_lines
+              max_number_of_lines: 150
+        ```
+    """
+
+    actual_number_of_lines = model.raw_code.count("\n") + 1
+
+    assert (
+        actual_number_of_lines <= max_number_of_lines
+    ), f"`{model.name}` has {actual_number_of_lines} lines, this is more than the maximum permitted number of lines ({max_number_of_lines})."
 
@@ -5122,7 +5111,14 @@

Source code in src/dbt_bouncer/checks/manifest/check_models.py -
772
+              
765
+766
+767
+768
+769
+770
+771
+772
 773
 774
 775
@@ -5156,55 +5152,48 @@ 

803 804 805 -806 -807 -808 -809 -810 -811 -812 -813

@pytest.mark.iterate_over_models
-@bouncer_check
-def check_model_max_upstream_dependencies(
-    request: TopRequest,
-    max_upstream_macros: Union[int, None] = None,
-    max_upstream_models: Union[int, None] = None,
-    max_upstream_sources: Union[int, None] = None,
-    model: Union[DbtBouncerModel, None] = None,
-    **kwargs,
-) -> None:
-    """
-    Limit the number of upstream dependencies a model has. Default values are 5 for models, 5 for macros, and 1 for sources.
-
-    Receives:
-        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
-        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
-        max_upstream_macros (Optional[int]): The maximum number of permitted upstream macros. Default: 5
-        max_upstream_models (Optional[int]): The maximum number of permitted upstream models. Default: 5
-        max_upstream_sources (Optional[int]): The maximum number of permitted upstream sources. Default: 1
-        model (DbtBouncerModel): The DbtBouncerModel object to check.
-
-    Example(s):
-        ```yaml
-        manifest_checks:
-            - name: check_model_max_upstream_dependencies
-              max_upstream_models: 3
-        ```
-    """
-
-    num_upstream_macros = len(list(model.depends_on.macros))
-    num_upstream_models = len([m for m in model.depends_on.nodes if m.split(".")[0] == "model"])
-    num_upstream_sources = len([m for m in model.depends_on.nodes if m.split(".")[0] == "source"])
-
-    assert (
-        num_upstream_macros <= max_upstream_macros  # type: ignore[operator]
-    ), f"`{model.name}` has {num_upstream_macros} upstream macros, which is more than the permitted maximum of {max_upstream_macros}."
-    assert (
-        num_upstream_models <= max_upstream_models  # type: ignore[operator]
-    ), f"`{model.name}` has {num_upstream_models} upstream models, which is more than the permitted maximum of {max_upstream_models}."
-    assert (
-        num_upstream_sources <= max_upstream_sources  # type: ignore[operator]
-    ), f"`{model.name}` has {num_upstream_sources} upstream sources, which is more than the permitted maximum of {max_upstream_sources}."
+806
@pytest.mark.iterate_over_models
+@bouncer_check
+def check_model_max_upstream_dependencies(
+    request: TopRequest,
+    max_upstream_macros: Union[int, None] = None,
+    max_upstream_models: Union[int, None] = None,
+    max_upstream_sources: Union[int, None] = None,
+    model: Union[DbtBouncerModel, None] = None,
+    **kwargs,
+) -> None:
+    """
+    Limit the number of upstream dependencies a model has. Default values are 5 for models, 5 for macros, and 1 for sources.
+
+    Receives:
+        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
+        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
+        max_upstream_macros (Optional[int]): The maximum number of permitted upstream macros. Default: 5
+        max_upstream_models (Optional[int]): The maximum number of permitted upstream models. Default: 5
+        max_upstream_sources (Optional[int]): The maximum number of permitted upstream sources. Default: 1
+        model (DbtBouncerModel): The DbtBouncerModel object to check.
+
+    Example(s):
+        ```yaml
+        manifest_checks:
+            - name: check_model_max_upstream_dependencies
+              max_upstream_models: 3
+        ```
+    """
+
+    num_upstream_macros = len(list(model.depends_on.macros))
+    num_upstream_models = len([m for m in model.depends_on.nodes if m.split(".")[0] == "model"])
+    num_upstream_sources = len([m for m in model.depends_on.nodes if m.split(".")[0] == "source"])
+
+    assert (
+        num_upstream_macros <= max_upstream_macros  # type: ignore[operator]
+    ), f"`{model.name}` has {num_upstream_macros} upstream macros, which is more than the permitted maximum of {max_upstream_macros}."
+    assert (
+        num_upstream_models <= max_upstream_models  # type: ignore[operator]
+    ), f"`{model.name}` has {num_upstream_models} upstream models, which is more than the permitted maximum of {max_upstream_models}."
+    assert (
+        num_upstream_sources <= max_upstream_sources  # type: ignore[operator]
+    ), f"`{model.name}` has {num_upstream_sources} upstream sources, which is more than the permitted maximum of {max_upstream_sources}."
 
@@ -5279,16 +5268,23 @@

Example(s):

manifest_checks:
     - name: check_model_names
-      include: ^intermediate
+      include: ^models/intermediate
       model_name_pattern: ^int_
     - name: check_model_names
-      include: ^staging
+      include: ^models/staging
       model_name_pattern: ^stg_
 

Source code in src/dbt_bouncer/checks/manifest/check_models.py -
823
+              
816
+817
+818
+819
+820
+821
+822
+823
 824
 825
 826
@@ -5312,45 +5308,38 @@ 

844 845 846 -847 -848 -849 -850 -851 -852 -853 -854

@pytest.mark.iterate_over_models
-@bouncer_check
-def check_model_names(
-    request: TopRequest,
-    model: Union[DbtBouncerModel, None] = None,
-    model_name_pattern: Union[None, str] = None,
-    **kwargs,
-) -> None:
-    """
-    Models must have a name that matches the supplied regex.
-
-    Receives:
-        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
-        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
-        model_name_pattern (str): Regexp the model name must match.
-        model (DbtBouncerModel): The DbtBouncerModel object to check.
-
-    Example(s):
-        ```yaml
-        manifest_checks:
-            - name: check_model_names
-              include: ^intermediate
-              model_name_pattern: ^int_
-            - name: check_model_names
-              include: ^staging
-              model_name_pattern: ^stg_
-        ```
-    """
-
-    assert (
-        re.compile(model_name_pattern.strip()).match(model.name) is not None
-    ), f"`{model.name}` does not match the supplied regex `{model_name_pattern.strip()})`."
+847
@pytest.mark.iterate_over_models
+@bouncer_check
+def check_model_names(
+    request: TopRequest,
+    model: Union[DbtBouncerModel, None] = None,
+    model_name_pattern: Union[None, str] = None,
+    **kwargs,
+) -> None:
+    """
+    Models must have a name that matches the supplied regex.
+
+    Receives:
+        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
+        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
+        model_name_pattern (str): Regexp the model name must match.
+        model (DbtBouncerModel): The DbtBouncerModel object to check.
+
+    Example(s):
+        ```yaml
+        manifest_checks:
+            - name: check_model_names
+              include: ^models/intermediate
+              model_name_pattern: ^int_
+            - name: check_model_names
+              include: ^models/staging
+              model_name_pattern: ^stg_
+        ```
+    """
+
+    assert (
+        re.compile(model_name_pattern.strip()).match(model.name) is not None
+    ), f"`{model.name}` does not match the supplied regex `{model_name_pattern.strip()})`."
 
@@ -5419,7 +5408,14 @@

Source code in src/dbt_bouncer/checks/manifest/check_models.py -
861
+              
854
+855
+856
+857
+858
+859
+860
+861
 862
 863
 864
@@ -5448,50 +5444,43 @@ 

887 888 889 -890 -891 -892 -893 -894 -895 -896 -897

@pytest.mark.iterate_over_models
-@bouncer_check
-def check_model_property_file_location(
-    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs
-) -> None:
-    """
-    Model properties files must follow the guidance provided by dbt [here](https://docs.getdbt.com/best-practices/how-we-structure/1-guide-overview).
-
-    Receives:
-        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
-        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
-        model (DbtBouncerModel): The DbtBouncerModel object to check.
+890
@pytest.mark.iterate_over_models
+@bouncer_check
+def check_model_property_file_location(
+    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs
+) -> None:
+    """
+    Model properties files must follow the guidance provided by dbt [here](https://docs.getdbt.com/best-practices/how-we-structure/1-guide-overview).
+
+    Receives:
+        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.
+        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.
+        model (DbtBouncerModel): The DbtBouncerModel object to check.
+
+    Example(s):
+        ```yaml
+        manifest_checks:
+            - name: check_model_property_file_location
+        ```
+    """
 
-    Example(s):
-        ```yaml
-        manifest_checks:
-            - name: check_model_property_file_location
-        ```
-    """
-
-    expected_substr = (
-        "_".join(model.path.split("/")[:-1])
-        .replace("staging", "stg")
-        .replace("intermediate", "int")
-        .replace("marts", "")
-    )
-    properties_yml_name = model.patch_path.split("/")[-1]
-
-    assert properties_yml_name.startswith(
-        "_"
-    ), f"The properties file for `{model.name}` (`{properties_yml_name}`) does not start with an underscore."
-    assert (
-        expected_substr in properties_yml_name
-    ), f"The properties file for `{model.name}` (`{properties_yml_name}`) does not contain the expected substring (`{expected_substr}`)."
-    assert properties_yml_name.endswith(
-        "__models.yml"
-    ), f"The properties file for `{model.name}` (`{properties_yml_name}`) does not end with `__models.yml`."
+    expected_substr = (
+        "_".join(model.original_file_path.split("/")[1:-1])
+        .replace("staging", "stg")
+        .replace("intermediate", "int")
+        .replace("marts", "")
+    )
+    properties_yml_name = model.patch_path.split("/")[-1]
+
+    assert properties_yml_name.startswith(
+        "_"
+    ), f"The properties file for `{model.name}` (`{properties_yml_name}`) does not start with an underscore."
+    assert (
+        expected_substr in properties_yml_name
+    ), f"The properties file for `{model.name}` (`{properties_yml_name}`) does not contain the expected substring (`{expected_substr}`)."
+    assert properties_yml_name.endswith(
+        "__models.yml"
+    ), f"The properties file for `{model.name}` (`{properties_yml_name}`) does not end with `__models.yml`."
 
@@ -5541,7 +5530,14 @@

Source code in src/dbt_bouncer/checks/manifest/check_models.py -
914
+              
907
+908
+909
+910
+911
+912
+913
+914
 915
 916
 917
@@ -5567,47 +5563,40 @@ 

937 938 939 -940 -941 -942 -943 -944 -945 -946 -947

@bouncer_check
-def check_model_test_coverage(
-    models: List[DbtBouncerModel],
-    request: TopRequest,
-    tests: List[DbtBouncerModel],
-    min_model_test_coverage_pct: Union[float, None] = None,
-    **kwargs,
-) -> None:
-    """
-    Set the minimum percentage of models that have at least one test.
-
-    Receives:
-        min_model_test_coverage_pct (float): The minimum percentage of models that must have at least one test. Default: 100
-
-    Example(s):
-        ```yaml
-        manifest_checks:
-            - name: check_model_test_coverage
-              min_model_test_coverage_pct: 90
-        ```
-    """
-
-    num_models = len(models)
-    models_with_tests = []
-    for model in models:
-        for test in tests:
-            if model.unique_id in test.depends_on.nodes:
-                models_with_tests.append(model.unique_id)
-    num_models_with_tests = len(set(models_with_tests))
-    model_test_coverage_pct = (num_models_with_tests / num_models) * 100
-
-    assert (
-        model_test_coverage_pct >= min_model_test_coverage_pct  # type: ignore[operator]
-    ), f"Only {model_test_coverage_pct}% of models have at least one test, this is less than the permitted minimum of {min_model_test_coverage_pct}%."
+940
@bouncer_check
+def check_model_test_coverage(
+    models: List[DbtBouncerModel],
+    request: TopRequest,
+    tests: List[DbtBouncerModel],
+    min_model_test_coverage_pct: Union[float, None] = None,
+    **kwargs,
+) -> None:
+    """
+    Set the minimum percentage of models that have at least one test.
+
+    Receives:
+        min_model_test_coverage_pct (float): The minimum percentage of models that must have at least one test. Default: 100
+
+    Example(s):
+        ```yaml
+        manifest_checks:
+            - name: check_model_test_coverage
+              min_model_test_coverage_pct: 90
+        ```
+    """
+
+    num_models = len(models)
+    models_with_tests = []
+    for model in models:
+        for test in tests:
+            if model.unique_id in test.depends_on.nodes:
+                models_with_tests.append(model.unique_id)
+    num_models_with_tests = len(set(models_with_tests))
+    model_test_coverage_pct = (num_models_with_tests / num_models) * 100
+
+    assert (
+        model_test_coverage_pct >= min_model_test_coverage_pct  # type: ignore[operator]
+    ), f"Only {model_test_coverage_pct}% of models have at least one test, this is less than the permitted minimum of {min_model_test_coverage_pct}%."
 
@@ -6660,7 +6649,7 @@

``` """ - path_cleaned = source.path.replace("models/staging", "") + path_cleaned = source.original_file_path.replace("models/staging", "") expected_substring = "_".join(path_cleaned.split("/")[:-1]) assert path_cleaned.split("/")[-1].startswith( @@ -6773,7 +6762,8 @@

331 332 333 -334

@pytest.mark.iterate_over_sources
+334
+335
@pytest.mark.iterate_over_sources
 @bouncer_check
 def check_source_used_by_models_in_same_directory(
     models: List[DbtBouncerModel],
@@ -6800,13 +6790,14 @@ 

for model in models: if ( source.unique_id in model.depends_on.nodes - and model.path.split("/")[:-1] != source.path.split("/")[1:-1] - ): - reffed_models_not_in_same_dir.append(model.unique_id.split(".")[0]) - - assert ( - len(reffed_models_not_in_same_dir) == 0 - ), f"Source `{source.source_name}.{source.name}` is referenced by models defined in a different directory: {reffed_models_not_in_same_dir}" + and model.original_file_path.split("/")[:-1] + != source.original_file_path.split("/")[:-1] + ): + reffed_models_not_in_same_dir.append(model.unique_id.split(".")[0]) + + assert ( + len(reffed_models_not_in_same_dir) == 0 + ), f"Source `{source.source_name}.{source.name}` is referenced by models defined in a different directory: {reffed_models_not_in_same_dir}"

@@ -6875,8 +6866,7 @@

Source code in src/dbt_bouncer/checks/manifest/check_sources.py -
341
-342
+              
342
 343
 344
 345
@@ -6901,33 +6891,34 @@ 

364 365 366 -367

@pytest.mark.iterate_over_sources
-@bouncer_check
-def check_source_used_by_only_one_model(
-    models: List[DbtBouncerModel],
-    request: TopRequest,
-    source: Union[DbtBouncerSource, None] = None,
-    **kwargs,
-) -> None:
-    """
-    Each source can be referenced by a maximum of one model.
-
-    Receives:
-        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.
-        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.
-        source (DbtBouncerSource): The DbtBouncerSource object to check.
-
-    Example(s):
-        ```yaml
-        manifest_checks:
-            - name: check_source_used_by_only_one_model
-        ```
-    """
-
-    num_refs = sum(source.unique_id in model.depends_on.nodes for model in models)
-    assert (
-        num_refs <= 1
-    ), f"Source `{source.source_name}.{source.name}` is referenced by more than one model."
+367
+368
@pytest.mark.iterate_over_sources
+@bouncer_check
+def check_source_used_by_only_one_model(
+    models: List[DbtBouncerModel],
+    request: TopRequest,
+    source: Union[DbtBouncerSource, None] = None,
+    **kwargs,
+) -> None:
+    """
+    Each source can be referenced by a maximum of one model.
+
+    Receives:
+        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.
+        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.
+        source (DbtBouncerSource): The DbtBouncerSource object to check.
+
+    Example(s):
+        ```yaml
+        manifest_checks:
+            - name: check_source_used_by_only_one_model
+        ```
+    """
+
+    num_refs = sum(source.unique_id in model.depends_on.nodes for model in models)
+    assert (
+        num_refs <= 1
+    ), f"Source `{source.source_name}.{source.name}` is referenced by more than one model."
 
diff --git a/latest/checks/checks_run_results/index.html b/latest/checks/checks_run_results/index.html index c9d02a44..fb902ff7 100644 --- a/latest/checks/checks_run_results/index.html +++ b/latest/checks/checks_run_results/index.html @@ -948,7 +948,7 @@

run_results_checks:
     - name: check_run_results_max_execution_time
-      include: ^staging # Not a good idea, here for demonstration purposes only
+      include: ^models/staging # Not a good idea, here for demonstration purposes only
       max_execution_time_seconds: 10
 

@@ -1013,7 +1013,7 @@

```yaml run_results_checks: - name: check_run_results_max_execution_time - include: ^staging # Not a good idea, here for demonstration purposes only + include: ^models/staging # Not a good idea, here for demonstration purposes only max_execution_time_seconds: 10 ``` """ diff --git a/latest/config_file/index.html b/latest/config_file/index.html index a563dc75..81db1fa3 100644 --- a/latest/config_file/index.html +++ b/latest/config_file/index.html @@ -636,7 +636,7 @@

Config file

manifest_checks: - name: check_macro_name_matches_file_name - name: check_model_names - include: ^staging + include: ^models/staging model_name_pattern: ^stg_

And the same config in toml:

@@ -649,24 +649,24 @@

Config file

[[tool.dbt-bouncer.manifest_checks]] name = "check_model_names" -include = "^staging" +include = "^models/staging" model_name_pattern = "^stg_"

For more example config files, see here.

Common arguments

Most (but not all) checks accept the following optional arguments:

    -
  • exclude: Regexp to match which paths to exclude.
  • -
  • include: Regexp to match which paths to include.
  • +
  • exclude: Regexp to match which original file paths to exclude.
  • +
  • include: Regexp to match which original file paths to include.

Example per resource type:

    -
  • Exposures: The path to the properties file where the source is defined, e.g. ^marts/finance will match exposures defined in ./models/marts/finance/_exposures.yml.
  • -
  • Macros: The path to the macro file, e.g. ^macros/system will match files like ./macros/system/generate_schema_name.sql.
  • -
  • Models: The path to the model file, e.g. ^marts will match files like ./models/marts/customers.sql.
  • -
  • Run results: The path to the file associated with the resource, e.g. ^finance/.*\.csv$ will match seeds in ./seeds/finance, ^staging will match models and tests in ./models/staging.
  • -
  • Sources: The path to the properties file where the source is defined, e.g. ^staging/crm will match sources defined in ./models/staging/crm/_crm__sources.yml.
  • -
  • Unit tests: The path to the properties file where the unit test is defined, e.g. ^staging/crm will match unit tests defined in ^staging/crm/_stg_crm__unit_tests.yml.
  • +
  • Exposures: The original file path to the properties file where the source is defined, e.g. ^models/marts/finance will match exposures defined in ./models/marts/finance/_exposures.yml.
  • +
  • Macros: The original file path to the macro file, e.g. ^macros/system will match files like ./macros/system/generate_schema_name.sql.
  • +
  • Models: The original file path to the model file, e.g. ^marts will match files like ./models/marts/customers.sql.
  • +
  • Run results: The original file path to the file associated with the resource, e.g. ^seeds/finance will match seeds in ./seeds/finance, ^models/staging will match models and tests in ./models/staging.
  • +
  • Sources: The original file path to the properties file where the source is defined, e.g. ^models/staging/crm will match sources defined in ./models/staging/crm/_crm__sources.yml.
  • +
  • Unit tests: The original file path to the properties file where the unit test is defined, e.g. ^models/staging/crm will match unit tests defined in ^staging/crm/_stg_crm__unit_tests.yml.

To determine if a check accepts these arguments view the Checks page.

@@ -675,18 +675,18 @@

Common arguments

# Specify `include` at the check level only
 manifest_checks:
   - name: check_model_names
-    include: ^staging
+    include: ^models/staging
     model_name_pattern: ^stg_
 
# Specify `include` at the check and global levels
-include: ^marts
+include: ^models/marts
 manifest_checks:
   - name: check_model_names
-    include: ^staging
+    include: ^models/staging
     model_name_pattern: ^stg_
 
# Specify `include` at the global level only
-include: ^staging
+include: ^models/staging
 manifest_checks:
   - name: check_model_names
     model_name_pattern: ^stg_
@@ -712,7 +712,7 @@ 

Common arguments

- August 26, 2024 + August 27, 2024
diff --git a/latest/search/search_index.json b/latest/search/search_index.json index 774bd2a2..c9fb0bfe 100644 --- a/latest/search/search_index.json +++ b/latest/search/search_index.json @@ -1 +1 @@ -{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#welcome-to-dbt-bouncer","title":"Welcome to dbt-bouncer","text":"

dbt-bouncer is an open-source tool that allows you to configure and enforce conventions for your dbt project. The conventions are run against dbt's artifact files (think ./target/manifest.json) resulting in speedy tests. Conventions can be specified in a .yml file, allowing maximum customisation to the conventions you wish to follow (or create \ud83d\ude00).

Check out our Getting Started guide.

"},{"location":"#terminology","title":"Terminology","text":"
  • Check: A check is a rule run against a dbt artifact.
  • Config file: A .yml file that specifies which checks to run along with any parameters.
  • dbt artifacts directory: The directory that contains the dbt artifacts (manifest.json, etc.), generally this is ./target.
"},{"location":"#about","title":"About","text":"

dbt-bouncer is free software, released under the MIT license. It originated at Xebia Data in Amsterdam, Netherlands. Source code is available on GitHub.

All contributions, in the form of bug reports, pull requests, feedback or discussion are welcome. See the contributing guide for more information.

"},{"location":"CONTRIBUTING/","title":"Contributing to dbt-bouncer","text":"

dbt-bouncer is open source software. Whether you are a seasoned open source contributor or a first-time committer, we welcome and encourage you to contribute code, documentation, ideas, or problem statements to this project.

"},{"location":"CONTRIBUTING/#about-this-document","title":"About this document","text":"

There are many ways to contribute to the ongoing development of dbt-bouncer, such as by participating in discussions and issues.

The rest of this document serves as a more granular guide for contributing code changes to dbt-bouncer (this repository). It is not intended as a guide for using dbt-bouncer, and some pieces assume a level of familiarity with Python development (virtualenvs, Poetry, etc). Specific code snippets in this guide assume you are using macOS or Linux and are comfortable with the command line.

If you get stuck, we're happy to help! Just open an issue or draft PR and we'll do our best to help out.

"},{"location":"CONTRIBUTING/#note","title":"Note","text":"
  • Branches: All pull requests from community contributors should target the main branch (default).
"},{"location":"CONTRIBUTING/#getting-the-code","title":"Getting the code","text":""},{"location":"CONTRIBUTING/#installing-git","title":"Installing git","text":"

You will need git in order to download and modify the dbt-bouncer source code. On macOS, the best way to download git is to just install Xcode.

"},{"location":"CONTRIBUTING/#contributors","title":"Contributors","text":"

You can contribute to dbt-bouncer by forking the dbt-bouncer repository. For a detailed overview on forking, check out the GitHub docs on forking. In short, you will need to:

  1. Fork the dbt-bouncer repository.
  2. Clone your fork locally.
  3. Check out a new branch for your proposed changes.
  4. Push changes to your fork.
  5. Open a pull request against godatadriven/dbt-bouncer from your forked repository.
"},{"location":"CONTRIBUTING/#setting-up-an-environment","title":"Setting up an environment","text":"

There are some tools that will be helpful to you in developing locally. While this is the list relevant for dbt-bouncer development, many of these tools are used commonly across open-source python projects.

"},{"location":"CONTRIBUTING/#tools","title":"Tools","text":"

These are the tools used in dbt-bouncer development and testing:

  • black for code formatting.
  • click to create our CLI interface.
  • GitHub Actions for automating tests and checks, once a PR is pushed to the dbt-bouncer repository.
  • make to run multiple setup or test steps in combination.
  • mypy for static type checking.
  • Poetry to manage our python virtual environment.
  • pre-commit to easily run those checks.
  • Pydantic to validate our configuration file.
  • pytest to define, discover, and run tests.

A deep understanding of these tools in not required to effectively contribute to dbt-bouncer, but we recommend checking out the attached documentation if you're interested in learning more about each one.

"},{"location":"CONTRIBUTING/#virtual-environments","title":"Virtual environments","text":"

We strongly recommend using virtual environments when developing code in dbt-bouncer. We recommend creating this virtualenv in the root of the dbt-bouncer repository. To create a new virtualenv, run:

poetry shell\n

This will create a new Python virtual environment.

"},{"location":"CONTRIBUTING/#setting-environment-variables","title":"Setting environment variables","text":"

Set required environment variables by copying .env.example to .env and updating the values.

"},{"location":"CONTRIBUTING/#running-dbt-bouncer-in-development","title":"Running dbt-bouncer in development","text":""},{"location":"CONTRIBUTING/#installation","title":"Installation","text":"

First make sure that you set up your virtualenv as described in Setting up an environment. Next, install dbt-bouncer, its dependencies and pre-commit:

poetry install\npoetry run pre-commit install\n

When installed in this way, any changes you make to your local copy of the source code will be reflected immediately in your next dbt-bouncer run.

"},{"location":"CONTRIBUTING/#running-dbt-bouncer","title":"Running dbt-bouncer","text":"

With your virtualenv activated, the dbt-bouncer script should point back to the source code you've cloned on your machine. You can verify this by running which dbt-bouncer. This command should show you a path to an executable in your virtualenv. You can run dbt-bouncer using the provided example configuration file via:

poetry run dbt-bouncer --config-file dbt-bouncer-example.yml\n
"},{"location":"CONTRIBUTING/#testing","title":"Testing","text":"

Once you're able to manually test that your code change is working as expected, it's important to run existing automated tests, as well as adding some new ones. These tests will ensure that: - Your code changes do not unexpectedly break other established functionality - Your code changes can handle all known edge cases - The functionality you're adding will keep working in the future

"},{"location":"CONTRIBUTING/#note_1","title":"Note","text":"
  • Generating dbt artifacts: If you change the configuration of the dbt project located in dbt_project then you will need to re-generate the dbt artifacts used in testing. To do so, run:
make build-artifacts\n
"},{"location":"CONTRIBUTING/#test-commands","title":"Test commands","text":"

There are a few methods for running tests locally.

"},{"location":"CONTRIBUTING/#makefile","title":"makefile","text":"

There are multiple targets in the makefile to run common test suites, most notably:

# Runs unit tests\nmake test-unit\n\n# Runs integration tests\nmake test-integration\n\n# Runs all tests\nmake test\n
"},{"location":"CONTRIBUTING/#pre-commit","title":"pre-commit","text":"

pre-commit takes care of running all code-checks for formatting and linting. Run poetry run pre-commit install to install pre-commit in your local environment. Once this is done you can use the git pre-commit hooks to ensure proper formatting and linting.

"},{"location":"CONTRIBUTING/#pytest","title":"pytest","text":"

Finally, you can also run a specific test or group of tests using pytest directly. With a virtualenv active and dev dependencies installed you can do things like:

# run all unit tests in a file\npoetry run pytest ./tests/unit/checks/catalog/test_columns.py\n\n# run a specific unit test\npoetry run pytest ./tests/unit/checks/catalog/test_columns.py::test_check_columns_are_documented_in_public_models\n

See pytest usage docs for an overview of useful command-line options.

"},{"location":"CONTRIBUTING/#assorted-development-tips","title":"Assorted development tips","text":"
  • Append # type: ignore to the end of a line if you need to disable mypy on that line.
"},{"location":"CONTRIBUTING/#submitting-a-pull-request","title":"Submitting a Pull Request","text":"

Code can be merged into the current development branch main by opening a pull request. If the proposal looks like it's on the right track, then a dbt-bouncer maintainer will review the PR. They may suggest code revision for style or clarity, or request that you add unit or integration test(s). These are good things! We believe that, with a little bit of help, anyone can contribute high-quality code. Once merged, your contribution will be available for the next release of dbt-bouncer.

Automated tests run via GitHub Actions. If you're a first-time contributor, all tests will require a maintainer to approve.

Once all tests are passing and your PR has been approved, a dbt-bouncer maintainer will merge your changes into the active development branch. And that's it! Happy developing :tada:

"},{"location":"cli/","title":"CLI","text":"

This page provides documentation for the dbt-bouncer CLI.

"},{"location":"cli/#dbt-bouncer","title":"dbt-bouncer","text":"

Usage:

dbt-bouncer [OPTIONS]\n

Options:

  --config-file PATH  Location of the YML config file.\n  --output-file PATH  Location of the json file where check metadata will be\n                      saved.\n  -v, --verbosity     Verbosity.\n  --version           Show the version and exit.\n  --help              Show this message and exit.\n
"},{"location":"cli/#exit-codes","title":"Exit codes","text":"

dbt-bouncer returns the following exit codes:

  • 0: All checks have succeeded.

  • 1:

    • At least one check has failed. Check the logs for more information.
    • A fatal error occurred during check setup or check execution. Check the logs for more information.
"},{"location":"config_file/","title":"Config file","text":"

dbt-bouncer requires a config file which determines what checks are run. The following options are available, in order of priority:

  1. A file passed via the --config-file CLI flag.
  2. A file named dbt-bouncer.yml in the current working directory.
  3. A [tool.dbt-bouncer] section in pyproject.toml.

Here is an example config file in yaml:

# [Optional] Directory where the dbt artifacts exists, generally the `target` directory inside a dbt project. Defaults to `./target`.\ndbt_artifacts_dir: target\n\nmanifest_checks:\n  - name: check_macro_name_matches_file_name\n  - name: check_model_names\n    include: ^staging\n    model_name_pattern: ^stg_\n

And the same config in toml:

[tool.dbt-bouncer]\n# [Optional] Directory where the dbt artifacts exists, generally the `target` directory inside a dbt project. Defaults to `./target`.\ndbt_artifacts_dir = \"target\"\n\n[[tool.dbt-bouncer.manifest_checks]]\nname = \"check_macro_name_matches_file_name\"\n\n[[tool.dbt-bouncer.manifest_checks]]\nname = \"check_model_names\"\ninclude = \"^staging\"\nmodel_name_pattern = \"^stg_\"\n

For more example config files, see here.

"},{"location":"config_file/#common-arguments","title":"Common arguments","text":"

Most (but not all) checks accept the following optional arguments:

  • exclude: Regexp to match which paths to exclude.
  • include: Regexp to match which paths to include.

Example per resource type:

  • Exposures: The path to the properties file where the source is defined, e.g. ^marts/finance will match exposures defined in ./models/marts/finance/_exposures.yml.
  • Macros: The path to the macro file, e.g. ^macros/system will match files like ./macros/system/generate_schema_name.sql.
  • Models: The path to the model file, e.g. ^marts will match files like ./models/marts/customers.sql.
  • Run results: The path to the file associated with the resource, e.g. ^finance/.*\\.csv$ will match seeds in ./seeds/finance, ^staging will match models and tests in ./models/staging.
  • Sources: The path to the properties file where the source is defined, e.g. ^staging/crm will match sources defined in ./models/staging/crm/_crm__sources.yml.
  • Unit tests: The path to the properties file where the unit test is defined, e.g. ^staging/crm will match unit tests defined in ^staging/crm/_stg_crm__unit_tests.yml.

To determine if a check accepts these arguments view the Checks page.

Note

exclude and include can be specified at both the check level and the global level. Should both levels be specified, then the check level is applied. All the below examples result in the check_model_names check being run on all models in ./models/staging:

# Specify `include` at the check level only\nmanifest_checks:\n  - name: check_model_names\n    include: ^staging\n    model_name_pattern: ^stg_\n
# Specify `include` at the check and global levels\ninclude: ^marts\nmanifest_checks:\n  - name: check_model_names\n    include: ^staging\n    model_name_pattern: ^stg_\n
# Specify `include` at the global level only\ninclude: ^staging\nmanifest_checks:\n  - name: check_model_names\n    model_name_pattern: ^stg_\n
"},{"location":"faq/","title":"FAQ","text":"How to set up `dbt-bouncer` in a monorepo? A monorepo may consist of one directory with a dbt project and other directories with unrelated code. It may be desired for `dbt-bouncer` to be configured from the root directory. Sample directory tree:
.\n\u251c\u2500\u2500 dbt-bouncer.yml\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 dbt-project\n\u2502   \u251c\u2500\u2500 models\n\u2502   \u251c\u2500\u2500 dbt_project.yml\n\u2502   \u2514\u2500\u2500 profiles.yml\n\u2514\u2500\u2500 package-a\n    \u251c\u2500\u2500 src\n    \u251c\u2500\u2500 tests\n    \u2514\u2500\u2500 package.json\n
To ease configuration you can use `exclude` or `include` at the global level (see [Config File](./config-file.md) for more details). For the above example `dbt-bouncer.yml` could be configured as:
dbt_artifacts_dir: dbt-project/target\ninclude: ^dbt-project\n\nmanifest_checks:\n    - name: check_exposure_based_on_non_public_models\n
`dbt-bouncer` can now be run from the root directory."},{"location":"getting_started/","title":"Getting Started","text":""},{"location":"getting_started/#how-to-run-dbt-bouncer","title":"How to run dbt-bouncer","text":"
  1. Generate dbt artifacts by running a dbt command:

    • dbt parse to generate a manifest.json artifact.
    • dbt docs generate to generate a catalog.json artifact (necessary if you are using catalog checks).
    • dbt run (or any other command that implies it e.g. dbt build) to generate a run_results.json artifact (necessary if you are using run results checks).
  2. Create a dbt-bouncer.yml config file, details here.

  3. Run dbt-bouncer to validate that your conventions are being maintained.

"},{"location":"getting_started/#python","title":"Python","text":"

Install from pypi.org:

pip install dbt-bouncer\n

Run:

dbt-bouncer --config-file <PATH_TO_CONFIG_FILE>\n
Running dbt-bouncer (X.X.X)...\nLoaded config from dbt-bouncer-example.yml...\nValidating conf...\n

dbt-bouncer also supports a verbose mode, run:

dbt-bouncer --config-file <PATH_TO_CONFIG_FILE> -v\n
Running dbt-bouncer (X.X.X)...\nconfig_file=PosixPath('dbt-bouncer-example.yml')\nconfig_file_source='COMMANDLINE'\nConfig file passed via command line: dbt-bouncer-example.yml\nLoading config from /home/pslattery/repos/dbt-bouncer/dbt-bouncer-example.yml...\nLoading config from dbt-bouncer-example.yml...\nLoaded config from dbt-bouncer-example.yml...\nconf={'dbt_artifacts_dir': 'dbt_project/target', 'catalog_checks': [{'name': 'check_column_name_complies_to_column_type', 'column_name_pattern': '^is_.*', 'exclude': '^staging', 'types': ['BOOLEAN']}]}\nValidating conf...\n
"},{"location":"getting_started/#github-actions","title":"GitHub Actions","text":"

Run dbt-bouncer as part of your CI pipeline:

steps:\n    ...\n\n    - uses: godatadriven/dbt-bouncer@vX.X\n      with:\n        config-file: ./<PATH_TO_CONFIG_FILE>\n        output-file: results.json # optional, default does not save a results file\n        send-pr-comment: true # optional, defaults to true\n        verbose: false # optional, defaults to false\n\n    ...\n

We recommend pinning both a major and minor version number.

"},{"location":"getting_started/#docker","title":"Docker","text":"

Run dbt-bouncer via Docker:

docker run --rm \\\n    --volume \"$PWD\":/app \\\n    ghcr.io/godatadriven/dbt-bouncer:vX.X.X \\\n    --config-file /app/<PATH_TO_CONFIG_FILE>\n
"},{"location":"getting_started/#pex","title":"Pex","text":"

You can also run the .pex (Python EXecutable) artifact directly once you have a python executable (3.8 -> 3.12) installed:

wget https://github.com/godatadriven/dbt-bouncer/releases/download/vX.X.X/dbt-bouncer.pex -O dbt-bouncer.pex\n\npython dbt-bouncer.pex --config-file $PWD/<PATH_TO_CONFIG_FILE>\n
"},{"location":"checks/","title":"Checks","text":"

dbt-bouncer runs checks against artifacts from dbt. These checks fall into three categories:

  • Manifest Checks
  • Catalog Checks
  • Run Results Checks
"},{"location":"checks/checks_catalog/","title":"Catalog Checks","text":"

Note

The below checks require both catalog.json and manifest.json to be present.

"},{"location":"checks/checks_catalog/#catalog.check_catalog_sources","title":"check_catalog_sources","text":""},{"location":"checks/checks_catalog/#catalog.check_catalog_sources.check_source_columns_are_all_documented","title":"check_source_columns_are_all_documented","text":"

All columns in a source should be included in the source's properties file, i.e. .yml file.

Receives:

Name Type Description catalog_source DbtBouncerCatalogNode

The DbtBouncerCatalogNode object to check.

exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

Example(s):

catalog_checks:\n    - name: check_source_columns_are_all_documented\n

Source code in src/dbt_bouncer/checks/catalog/check_catalog_sources.py
@pytest.mark.iterate_over_catalog_sources\n@bouncer_check\ndef check_source_columns_are_all_documented(\n    sources: List[DbtBouncerSource],\n    request: TopRequest,\n    catalog_source: Union[DbtBouncerCatalogNode, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    All columns in a source should be included in the source's properties file, i.e. `.yml` file.\n\n    Receives:\n        catalog_source (DbtBouncerCatalogNode): The DbtBouncerCatalogNode object to check.\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n\n    Example(s):\n        ```yaml\n        catalog_checks:\n            - name: check_source_columns_are_all_documented\n        ```\n    \"\"\"\n\n    source = [s for s in sources if s.unique_id == catalog_source.unique_id][0]\n    undocumented_columns = [\n        v.name for _, v in catalog_source.columns.items() if v.name not in source.columns.keys()\n    ]\n    assert (\n        not undocumented_columns\n    ), f\"`{catalog_source.unique_id}` has columns that are not included in the sources properties file: {undocumented_columns}\"\n
"},{"location":"checks/checks_catalog/#catalog.check_columns","title":"check_columns","text":""},{"location":"checks/checks_catalog/#catalog.check_columns.check_column_description_populated","title":"check_column_description_populated","text":"

Columns must have a populated description.

Receives:

Name Type Description catalog_node CatalogTable

The CatalogTable object to check.

exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

Example(s):

manifest_checks:\n    - name: check_column_description_populated\n      include: ^marts\n

Source code in src/dbt_bouncer/checks/catalog/check_columns.py
@pytest.mark.iterate_over_catalog_nodes\n@bouncer_check\ndef check_column_description_populated(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    catalog_node: Union[CatalogTable, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Columns must have a populated description.\n\n    Receives:\n        catalog_node (CatalogTable): The CatalogTable object to check.\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_column_description_populated\n              include: ^marts\n        ```\n    \"\"\"\n\n    if catalog_node.unique_id.split(\".\")[0] == \"model\":\n        model = [m for m in models if m.unique_id == catalog_node.unique_id][0]\n        non_complying_columns = []\n        for _, v in catalog_node.columns.items():\n            if (\n                model.columns.get(v.name) is None\n                or len(model.columns[v.name].description.strip()) <= 4\n            ):\n                non_complying_columns.append(v.name)\n\n        assert (\n            not non_complying_columns\n        ), f\"`{catalog_node.unique_id.split('.')[-1]}` has columns that do not have a populated description: {non_complying_columns}\"\n
"},{"location":"checks/checks_catalog/#catalog.check_columns.check_column_name_complies_to_column_type","title":"check_column_name_complies_to_column_type","text":"

Columns with specified data types must comply to the specified regexp naming pattern.

Receives:

Name Type Description catalog_node CatalogTable

The CatalogTable object to check.

column_name_pattern None

(str): Regex pattern to match the model name.

exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

Example(s):

catalog_checks:\n    # DATE columns must end with \"_date\"\n    - name: check_column_name_complies_to_column_type\n      column_name_pattern: .*_date$\n      types:\n        - DATE\n
catalog_checks:\n    # BOOLEAN columns must start with \"is_\"\n    - name: check_column_name_complies_to_column_type\n      column_name_pattern: ^is_.*\n      types:\n        - BOOLEAN\n
catalog_checks:\n    # Columns of all types must consist of lowercase letters and underscores. Note that the specified types depend on the underlying database.\n    - name: check_column_name_complies_to_column_type\n      column_name_pattern: ^[a-z_]*$\n      types:\n        - BIGINT\n        - BOOLEAN\n        - DATE\n        - DOUBLE\n        - INTEGER\n        - VARCHAR\n

Source code in src/dbt_bouncer/checks/catalog/check_columns.py
@pytest.mark.iterate_over_catalog_nodes\n@bouncer_check\ndef check_column_name_complies_to_column_type(\n    request: TopRequest,\n    catalog_node: Union[CatalogTable, None] = None,\n    column_name_pattern: Union[None, str] = None,\n    types: Union[List[str], None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Columns with specified data types must comply to the specified regexp naming pattern.\n\n    Receives:\n        catalog_node (CatalogTable): The CatalogTable object to check.\n        column_name_pattern: (str): Regex pattern to match the model name.\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n\n    Example(s):\n        ```yaml\n        catalog_checks:\n            # DATE columns must end with \"_date\"\n            - name: check_column_name_complies_to_column_type\n              column_name_pattern: .*_date$\n              types:\n                - DATE\n        ```\n        ```yaml\n        catalog_checks:\n            # BOOLEAN columns must start with \"is_\"\n            - name: check_column_name_complies_to_column_type\n              column_name_pattern: ^is_.*\n              types:\n                - BOOLEAN\n        ```\n        ```yaml\n        catalog_checks:\n            # Columns of all types must consist of lowercase letters and underscores. Note that the specified types depend on the underlying database.\n            - name: check_column_name_complies_to_column_type\n              column_name_pattern: ^[a-z_]*$\n              types:\n                - BIGINT\n                - BOOLEAN\n                - DATE\n                - DOUBLE\n                - INTEGER\n                - VARCHAR\n        ```\n    \"\"\"\n\n    non_complying_columns = [\n        v.name\n        for _, v in catalog_node.columns.items()\n        if v.type in types and re.compile(column_name_pattern.strip()).match(v.name) is None  # type: ignore[operator]\n    ]\n\n    assert (\n        not non_complying_columns\n    ), f\"`{catalog_node.unique_id.split('.')[-1]}` has columns that don't comply with the specified regexp pattern (`{column_name_pattern}`): {non_complying_columns}\"\n
"},{"location":"checks/checks_catalog/#catalog.check_columns.check_columns_are_all_documented","title":"check_columns_are_all_documented","text":"

All columns in a model should be included in the model's properties file, i.e. .yml file.

Receives:

Name Type Description catalog_node CatalogTable

The CatalogTable object to check.

exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

Example(s):

catalog_checks:\n    - name: check_columns_are_all_documented\n

Source code in src/dbt_bouncer/checks/catalog/check_columns.py
@pytest.mark.iterate_over_catalog_nodes\n@bouncer_check\ndef check_columns_are_all_documented(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    catalog_node: Union[CatalogTable, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    All columns in a model should be included in the model's properties file, i.e. `.yml` file.\n\n    Receives:\n        catalog_node (CatalogTable): The CatalogTable object to check.\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n\n    Example(s):\n        ```yaml\n        catalog_checks:\n            - name: check_columns_are_all_documented\n        ```\n    \"\"\"\n\n    if catalog_node.unique_id.split(\".\")[0] == \"model\":\n        model = [m for m in models if m.unique_id == catalog_node.unique_id][0]\n        undocumented_columns = [\n            v.name for _, v in catalog_node.columns.items() if v.name not in model.columns.keys()\n        ]\n        assert (\n            not undocumented_columns\n        ), f\"`{catalog_node.unique_id.split('.')[-1]}` has columns that are not included in the models properties file: {undocumented_columns}\"\n
"},{"location":"checks/checks_catalog/#catalog.check_columns.check_columns_are_documented_in_public_models","title":"check_columns_are_documented_in_public_models","text":"

Columns should have a populated description in public models.

Receives:

Name Type Description catalog_node CatalogTable

The CatalogTable object to check.

exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

Example(s):

catalog_checks:\n    - name: check_columns_are_documented_in_public_models\n

Source code in src/dbt_bouncer/checks/catalog/check_columns.py
@pytest.mark.iterate_over_catalog_nodes\n@bouncer_check\ndef check_columns_are_documented_in_public_models(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    catalog_node: Union[CatalogTable, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Columns should have a populated description in public models.\n\n    Receives:\n        catalog_node (CatalogTable): The CatalogTable object to check.\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n\n    Example(s):\n        ```yaml\n        catalog_checks:\n            - name: check_columns_are_documented_in_public_models\n        ```\n    \"\"\"\n\n    if catalog_node.unique_id.split(\".\")[0] == \"model\":\n        model = [m for m in models if m.unique_id == catalog_node.unique_id][0]\n        non_complying_columns = []\n        for k, v in catalog_node.columns.items():\n            if model.access.value == \"public\":\n                column_config = model.columns.get(v.name)\n                if column_config is None or len(column_config.description.strip()) < 4:\n                    non_complying_columns.append(v.name)\n\n        assert (\n            not non_complying_columns\n        ), f\"`{catalog_node.unique_id.split('.')[-1]}` is a public model but has columns that don't have a populated description: {non_complying_columns}\"\n
"},{"location":"checks/checks_catalog/#catalog.check_columns.check_column_has_specified_test","title":"check_column_has_specified_test","text":"

Columns that match the specified regexp pattern must have a specified test.

Receives:

Name Type Description catalog_node CatalogTable

The CatalogTable object to check.

column_name_pattern str

Regex pattern to match the column name.

exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

test_name str

Name of the test to check for.

Example(s):

catalog_checks:\n    - name: check_column_has_specified_test\n      column_name_pattern: ^is_.*\n      test_name: not_null\n

Source code in src/dbt_bouncer/checks/catalog/check_columns.py
@pytest.mark.iterate_over_catalog_nodes\n@bouncer_check\ndef check_column_has_specified_test(\n    request: TopRequest,\n    tests: List[DbtBouncerTest],\n    catalog_node: Union[CatalogTable, None] = None,\n    column_name_pattern: Union[None, str] = None,\n    test_name: Union[None, str] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Columns that match the specified regexp pattern must have a specified test.\n\n    Receives:\n        catalog_node (CatalogTable): The CatalogTable object to check.\n        column_name_pattern (str): Regex pattern to match the column name.\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        test_name (str): Name of the test to check for.\n\n    Example(s):\n        ```yaml\n        catalog_checks:\n            - name: check_column_has_specified_test\n              column_name_pattern: ^is_.*\n              test_name: not_null\n        ```\n    \"\"\"\n\n    columns_to_check = [\n        v.name\n        for _, v in catalog_node.columns.items()\n        if re.compile(column_name_pattern.strip()).match(v.name) is not None\n    ]\n    relevant_tests = [\n        t\n        for t in tests\n        if t.test_metadata.name == test_name and t.attached_node == catalog_node.unique_id\n    ]\n    non_complying_columns = [\n        c\n        for c in columns_to_check\n        if f\"{catalog_node.unique_id}.{c}\"\n        not in [f\"{t.attached_node}.{t.column_name}\" for t in relevant_tests]\n    ]\n\n    assert (\n        not non_complying_columns\n    ), f\"`{catalog_node.unique_id.split('.')[-1]}` has columns that should have a `{test_name}` test: {non_complying_columns}\"\n
"},{"location":"checks/checks_manifest/","title":"Manifest Checks","text":"

Note

The below checks require manifest.json to be present.

"},{"location":"checks/checks_manifest/#manifest.check_exposures","title":"check_exposures","text":""},{"location":"checks/checks_manifest/#manifest.check_exposures.check_exposure_based_on_non_public_models","title":"check_exposure_based_on_non_public_models","text":"

Exposures should be based on public models only.

Receives:

Name Type Description exclude Optional[str]

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.

exposure Exposures

The Exposures object to check.

include Optional[str]

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.

Example(s):

manifest_checks:\n    - name: check_exposure_based_on_non_public_models\n

Source code in src/dbt_bouncer/checks/manifest/check_exposures.py
@pytest.mark.iterate_over_exposures\n@bouncer_check\ndef check_exposure_based_on_non_public_models(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    exposure: Union[Exposures, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Exposures should be based on public models only.\n\n    Receives:\n        exclude (Optional[str]): 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.\n        exposure (Exposures): The Exposures object to check.\n        include (Optional[str]): 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.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_exposure_based_on_non_public_models\n        ```\n    \"\"\"\n\n    non_public_upstream_dependencies = []\n    for model in exposure.depends_on.nodes:\n        if model.split(\".\")[0] == \"model\" and model.split(\".\")[1] == exposure.package_name:\n            model = [m for m in models if m.unique_id == model][0]\n            if model.access.value != \"public\":\n                non_public_upstream_dependencies.append(model.name)\n\n    assert (\n        not non_public_upstream_dependencies\n    ), f\"`{exposure.name}` is based on a model(s) that is not public: {non_public_upstream_dependencies}.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_exposures.check_exposure_based_on_view","title":"check_exposure_based_on_view","text":"

Exposures should not be based on views.

Receives:

Name Type Description exclude Optional[str]

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.

exposure Exposures

The Exposures object to check.

include Optional[str]

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.

materializations_to_include Optional[List[str]]

List of materializations to include in the check. If not provided, defaults to ephemeral and view.

Example(s):

manifest_checks:\n    - name: check_exposure_based_on_view\n
manifest_checks:\n    - name: check_exposure_based_on_view\n      materializations_to_include:\n        - ephemeral\n        - my_custom_materialization\n        - view\n

Source code in src/dbt_bouncer/checks/manifest/check_exposures.py
@pytest.mark.iterate_over_exposures\n@bouncer_check\ndef check_exposure_based_on_view(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    exposure: Union[Exposures, None] = None,\n    materializations_to_include: Union[List[str], None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Exposures should not be based on views.\n\n    Receives:\n        exclude (Optional[str]): 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.\n        exposure (Exposures): The Exposures object to check.\n        include (Optional[str]): 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.\n        materializations_to_include (Optional[List[str]]): List of materializations to include in the check. If not provided, defaults to `ephemeral` and `view`.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_exposure_based_on_view\n        ```\n        ```yaml\n        manifest_checks:\n            - name: check_exposure_based_on_view\n              materializations_to_include:\n                - ephemeral\n                - my_custom_materialization\n                - view\n        ```\n    \"\"\"\n\n    non_table_upstream_dependencies = []\n    for model in exposure.depends_on.nodes:\n        if model.split(\".\")[0] == \"model\" and model.split(\".\")[1] == exposure.package_name:\n            model = [m for m in models if m.unique_id == model][0]\n            if model.config.materialized in materializations_to_include:  # type: ignore[operator]\n                non_table_upstream_dependencies.append(model.name)\n\n    assert (\n        not non_table_upstream_dependencies\n    ), f\"`{exposure.name}` is based on a model that is not a table: {non_table_upstream_dependencies}.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_lineage","title":"check_lineage","text":""},{"location":"checks/checks_manifest/#manifest.check_lineage.check_lineage_permitted_upstream_models","title":"check_lineage_permitted_upstream_models","text":"

Upstream models must have a path that matches the provided upstream_path_pattern.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

upstream_path_pattern str

Regexp pattern to match the upstream model(s) path.

Example(s):

manifest_checks:\n    - name: check_lineage_permitted_upstream_models\n      include: ^staging\n      upstream_path_pattern: $^\n    - name: check_lineage_permitted_upstream_models\n      include: ^intermediate\n      upstream_path_pattern: ^staging|^intermediate\n    - name: check_lineage_permitted_upstream_models\n      include: ^marts\n      upstream_path_pattern: ^staging|^intermediate\n

Source code in src/dbt_bouncer/checks/manifest/check_lineage.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_lineage_permitted_upstream_models(\n    manifest_obj: DbtBouncerManifest,\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    model: Union[DbtBouncerModel, None] = None,\n    upstream_path_pattern: Union[None, str] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Upstream models must have a path that matches the provided `upstream_path_pattern`.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n        upstream_path_pattern (str): Regexp pattern to match the upstream model(s) path.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_lineage_permitted_upstream_models\n              include: ^staging\n              upstream_path_pattern: $^\n            - name: check_lineage_permitted_upstream_models\n              include: ^intermediate\n              upstream_path_pattern: ^staging|^intermediate\n            - name: check_lineage_permitted_upstream_models\n              include: ^marts\n              upstream_path_pattern: ^staging|^intermediate\n        ```\n    \"\"\"\n\n    upstream_models = [\n        x\n        for x in model.depends_on.nodes\n        if x.split(\".\")[0] == \"model\"\n        and x.split(\".\")[1] == manifest_obj.manifest.metadata.project_name\n    ]\n    not_permitted_upstream_models = [\n        upstream_model\n        for upstream_model in upstream_models\n        if re.compile(upstream_path_pattern.strip()).match(\n            [m for m in models if m.unique_id == upstream_model][0].path\n        )\n        is None\n    ]\n    assert (\n        not not_permitted_upstream_models\n    ), f\"`{model.name}` references upstream models that are not permitted: {[m.split('.')[-1] for m in not_permitted_upstream_models]}.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_lineage.check_lineage_seed_cannot_be_used","title":"check_lineage_seed_cannot_be_used","text":"

Seed cannot be referenced in models with a path that matches the specified include config.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_lineage_seed_cannot_be_used\n      include: ^intermediate|^marts\n

Source code in src/dbt_bouncer/checks/manifest/check_lineage.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_lineage_seed_cannot_be_used(\n    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Seed cannot be referenced in models with a path that matches the specified `include` config.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_lineage_seed_cannot_be_used\n              include: ^intermediate|^marts\n        ```\n    \"\"\"\n\n    assert not [\n        x for x in model.depends_on.nodes if x.split(\".\")[0] == \"seed\"\n    ], f\"`{model.name}` references a seed even though this is not permitted.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_lineage.check_lineage_source_cannot_be_used","title":"check_lineage_source_cannot_be_used","text":"

Sources cannot be referenced in models with a path that matches the specified include config.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_lineage_source_cannot_be_used\n      include: ^intermediate|^marts\n

Source code in src/dbt_bouncer/checks/manifest/check_lineage.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_lineage_source_cannot_be_used(\n    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Sources cannot be referenced in models with a path that matches the specified `include` config.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_lineage_source_cannot_be_used\n              include: ^intermediate|^marts\n        ```\n    \"\"\"\n\n    assert not [\n        x for x in model.depends_on.nodes if x.split(\".\")[0] == \"source\"\n    ], f\"`{model.name}` references a source even though this is not permitted.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_macros","title":"check_macros","text":""},{"location":"checks/checks_manifest/#manifest.check_macros.check_macro_arguments_description_populated","title":"check_macro_arguments_description_populated","text":"

Macro arguments must have a populated description.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.

macro Macros

The Macros object to check.

Example(s):

manifest_checks:\n    - name: check_macro_arguments_description_populated\n
# Only \"common\" macros need to have their arguments populated\nmanifest_checks:\n    - name: check_macro_arguments_description_populated\n      include: ^macros/common\n

Source code in src/dbt_bouncer/checks/manifest/check_macros.py
@pytest.mark.iterate_over_macros\n@bouncer_check\ndef check_macro_arguments_description_populated(\n    request: TopRequest, macro: Union[Macros, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Macro arguments must have a populated description.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.\n        macro (Macros): The Macros object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_macro_arguments_description_populated\n        ```\n        ```yaml\n        # Only \"common\" macros need to have their arguments populated\n        manifest_checks:\n            - name: check_macro_arguments_description_populated\n              include: ^macros/common\n        ```\n    \"\"\"\n\n    environment = jinja2.Environment(extensions=[TagExtension])\n    ast = environment.parse(macro.macro_sql)\n\n    # Assume macro is a \"true\" macro, if not see if it's a generic test\n    try:\n        macro_arguments = [a.name for a in ast.body[0].args]  # type: ignore[attr-defined]\n    except AttributeError:\n        test_macro = [x for x in ast.body if not isinstance(x.nodes[0], jinja2.nodes.Call)][0]  # type: ignore[attr-defined]\n        macro_arguments = [x.name for x in test_macro.nodes if isinstance(x, jinja2.nodes.Name)]  # type: ignore[attr-defined]\n\n    # macro_arguments: List of args parsed from macro SQL\n    # macro.arguments: List of args manually added to the properties file\n\n    non_complying_args = []\n    for arg in macro_arguments:\n        macro_doc_raw = [x for x in macro.arguments if x.name == arg]\n        if macro_doc_raw == []:\n            non_complying_args.append(arg)\n        elif (\n            arg not in [x.name for x in macro.arguments]\n            or len(macro_doc_raw[0].description.strip()) <= 4\n        ):\n            non_complying_args.append(arg)\n\n    assert (\n        non_complying_args == []\n    ), f\"Macro `{macro.name}` does not have a populated description for the following argument(s): {non_complying_args}.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_macros.check_macro_code_does_not_contain_regexp_pattern","title":"check_macro_code_does_not_contain_regexp_pattern","text":"

The raw code for a macro must not match the specified regexp pattern.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.

macro Macros

The Macros object to check.

regexp_pattern str

The regexp pattern that should not be matched by the macro code.

Example(s):

manifest_checks:\n    # Prefer `coalesce` over `ifnull`: https://docs.sqlfluff.com/en/stable/rules.html#sqlfluff.rules.sphinx.Rule_CV02\n    - name: check_macro_code_does_not_contain_regexp_pattern\n      regexp_pattern: .*[i][f][n][u][l][l].*\n

Source code in src/dbt_bouncer/checks/manifest/check_macros.py
@pytest.mark.iterate_over_macros\n@bouncer_check\ndef check_macro_code_does_not_contain_regexp_pattern(\n    request: TopRequest,\n    macro: Union[Macros, None] = None,\n    regexp_pattern: Union[None, str] = None,\n    **kwargs,\n) -> None:\n    \"\"\"The raw code for a macro must not match the specified regexp pattern.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.\n        macro (Macros): The Macros object to check.\n        regexp_pattern (str): The regexp pattern that should not be matched by the macro code.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            # Prefer `coalesce` over `ifnull`: https://docs.sqlfluff.com/en/stable/rules.html#sqlfluff.rules.sphinx.Rule_CV02\n            - name: check_macro_code_does_not_contain_regexp_pattern\n              regexp_pattern: .*[i][f][n][u][l][l].*\n        ```\n    \"\"\"\n\n    assert (\n        re.compile(regexp_pattern.strip(), flags=re.DOTALL).match(macro.macro_sql) is None\n    ), f\"Macro `{macro.name}` contains a banned string: `{regexp_pattern.strip()}`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_macros.check_macro_description_populated","title":"check_macro_description_populated","text":"

Macros must have a populated description.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.

macro Macros

The Macros object to check.

Example(s):

manifest_checks:\n    - name: check_macro_description_populated\n
# Only \"common\" macros need to have a populated description\nmanifest_checks:\n    - name: check_macro_description_populated\n      include: ^macros/common\n

Source code in src/dbt_bouncer/checks/manifest/check_macros.py
@pytest.mark.iterate_over_macros\n@bouncer_check\ndef check_macro_description_populated(\n    request: TopRequest, macro: Union[Macros, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Macros must have a populated description.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.\n        macro (Macros): The Macros object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_macro_description_populated\n        ```\n        ```yaml\n        # Only \"common\" macros need to have a populated description\n        manifest_checks:\n            - name: check_macro_description_populated\n              include: ^macros/common\n        ```\n    \"\"\"\n\n    assert (\n        len(macro.description.strip()) > 4\n    ), f\"Macro `{macro.name}` does not have a populated description.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_macros.check_macro_max_number_of_lines","title":"check_macro_max_number_of_lines","text":"

Macros may not have more than the specified number of lines.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.

macro Macros

The Macros object to check.

max_number_of_lines int

The maximum number of permitted lines. Default: 50.

Example(s):

manifest_checks:\n    - name: check_macro_max_number_of_lines\n
manifest_checks:\n    - name: check_macro_max_number_of_lines\n      max_number_of_lines: 100\n

Source code in src/dbt_bouncer/checks/manifest/check_macros.py
@pytest.mark.iterate_over_macros\n@bouncer_check\ndef check_macro_max_number_of_lines(\n    request: TopRequest,\n    macro: Union[Macros, None] = None,\n    max_number_of_lines: Union[int, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Macros may not have more than the specified number of lines.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.\n        macro (Macros): The Macros object to check.\n        max_number_of_lines (int): The maximum number of permitted lines. Default: 50.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_macro_max_number_of_lines\n        ```\n        ```yaml\n        manifest_checks:\n            - name: check_macro_max_number_of_lines\n              max_number_of_lines: 100\n        ```\n    \"\"\"\n\n    actual_number_of_lines = macro.macro_sql.count(\"\\n\") + 1\n\n    assert (\n        actual_number_of_lines <= max_number_of_lines\n    ), f\"Macro `{macro.name}` has {actual_number_of_lines} lines, this is more than the maximum permitted number of lines ({max_number_of_lines}).\"\n
"},{"location":"checks/checks_manifest/#manifest.check_macros.check_macro_name_matches_file_name","title":"check_macro_name_matches_file_name","text":"

Macros names must be the same as the file they are contained in.

Generic tests are also macros, however to document these tests the \"name\" value must be precededed with \"test_\".

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.

macro Macros

The Macros object to check.

Example(s):

manifest_checks:\n    - name: check_macro_name_matches_file_name\n

Source code in src/dbt_bouncer/checks/manifest/check_macros.py
@pytest.mark.iterate_over_macros\n@bouncer_check\ndef check_macro_name_matches_file_name(\n    request: TopRequest, macro: Union[Macros, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Macros names must be the same as the file they are contained in.\n\n    Generic tests are also macros, however to document these tests the \"name\" value must be precededed with \"test_\".\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.\n        macro (Macros): The Macros object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_macro_name_matches_file_name\n        ```\n    \"\"\"\n\n    if macro.name.startswith(\"test_\"):\n        assert (\n            macro.name[5:] == macro.path.split(\"/\")[-1].split(\".\")[0]\n        ), f\"Macro `{macro.unique_id}` is not in a file named `{macro.name[5:]}.sql`.\"\n    else:\n        assert (\n            macro.name == macro.path.split(\"/\")[-1].split(\".\")[0]\n        ), f\"Macro `{macro.name}` is not in a file of the same name.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_macros.check_macro_property_file_location","title":"check_macro_property_file_location","text":"

Macro properties files must follow the guidance provided by dbt here.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.

macro Macros

The Macros object to check.

Example(s):

manifest_checks:\n    - name: check_macro_property_file_location\n

Source code in src/dbt_bouncer/checks/manifest/check_macros.py
@pytest.mark.iterate_over_macros\n@bouncer_check\ndef check_macro_property_file_location(\n    request: TopRequest, macro: Union[Macros, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Macro properties files must follow the guidance provided by dbt [here](https://docs.getdbt.com/best-practices/how-we-structure/5-the-rest-of-the-project#how-we-use-the-other-folders).\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.\n        macro (Macros): The Macros object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_macro_property_file_location\n        ```\n    \"\"\"\n\n    expected_substr = \"_\".join(macro.path[6:].split(\"/\")[:-1])\n    properties_yml_name = macro.patch_path.split(\"/\")[-1]\n\n    if macro.path.startswith(\"tests/\"):  # Do not check generic tests (which are also macros)\n        pass\n    elif expected_substr == \"\":  # i.e. macro in ./macros\n        assert (\n            properties_yml_name == \"_macros.yml\"\n        ), f\"The properties file for `{macro.name}` (`{properties_yml_name}`) should be `_macros.yml`.\"\n    else:\n        assert properties_yml_name.startswith(\n            \"_\"\n        ), f\"The properties file for `{macro.name}` (`{properties_yml_name}`) does not start with an underscore.\"\n        assert (\n            expected_substr in properties_yml_name\n        ), f\"The properties file for `{macro.name}` (`{properties_yml_name}`) does not contain the expected substring (`{expected_substr}`).\"\n        assert properties_yml_name.endswith(\n            \"__macros.yml\"\n        ), f\"The properties file for `{macro.name.name}` (`{properties_yml_name}`) does not end with `__macros.yml`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_metadata","title":"check_metadata","text":""},{"location":"checks/checks_manifest/#manifest.check_metadata.check_project_name","title":"check_project_name","text":"

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>.

Receives:

Type Description None

project_name_pattern str: Regex pattern to match the project name.

Example(s):

manifest_checks:\n    - name: check_project_name\n      project_name_pattern: ^awesome_company_\n

Source code in src/dbt_bouncer/checks/manifest/check_metadata.py
@bouncer_check\ndef check_project_name(\n    manifest_obj: DbtBouncerManifest,\n    request: TopRequest,\n    project_name_pattern: Union[None, str] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    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>`.\n\n    Receives:\n        project_name_pattern str: Regex pattern to match the project name.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_project_name\n              project_name_pattern: ^awesome_company_\n        ```\n    \"\"\"\n\n    assert (\n        re.compile(project_name_pattern.strip()).match(manifest_obj.manifest.metadata.project_name)\n        is not None\n    ), f\"Project name (`{manifest_obj.manifest.metadata.project_name}`) does not conform to the supplied regex `({project_name_pattern.strip()})`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models","title":"check_models","text":""},{"location":"checks/checks_manifest/#manifest.check_models.check_model_access","title":"check_model_access","text":"

Models must have the specified access attribute. Requires dbt 1.7+.

Receives:

Name Type Description access Literal['private', 'protected', 'public']

The access level to check for.

exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    # Align with dbt best practices that marts should be `public`, everything else should be `protected`\n    - name: check_model_access\n      access: protected\n      include: ^intermediate\n    - name: check_model_access\n      access: public\n      include: ^marts\n    - name: check_model_access\n      access: protected\n      include: ^staging\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_access(\n    request: TopRequest,\n    access: Union[None, str] = None,\n    model: Union[DbtBouncerModel, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Models must have the specified access attribute. Requires dbt 1.7+.\n\n    Receives:\n        access (Literal[\"private\", \"protected\", \"public\"]): The access level to check for.\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            # Align with dbt best practices that marts should be `public`, everything else should be `protected`\n            - name: check_model_access\n              access: protected\n              include: ^intermediate\n            - name: check_model_access\n              access: public\n              include: ^marts\n            - name: check_model_access\n              access: protected\n              include: ^staging\n        ```\n    \"\"\"\n\n    assert (\n        model.access.value == access\n    ), f\"`{model.name}` has `{model.access.value}` access, it should have access `{access}`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_contract_enforced_for_public_model","title":"check_model_contract_enforced_for_public_model","text":"

Public models must have contracts enforced.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_contract_enforced_for_public_model\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_contract_enforced_for_public_model(\n    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Public models must have contracts enforced.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_contract_enforced_for_public_model\n        ```\n    \"\"\"\n\n    if model.access.value == \"public\":\n        assert (\n            model.contract.enforced is True\n        ), f\"`{model.name}` is a public model but does not have contracts enforced.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_description_populated","title":"check_model_description_populated","text":"

Models must have a populated description.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_description_populated\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_description_populated(\n    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Models must have a populated description.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_description_populated\n        ```\n    \"\"\"\n\n    assert (\n        len(model.description.strip()) > 4\n    ), f\"`{model.name}` does not have a populated description.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_documentation_coverage","title":"check_model_documentation_coverage","text":"

Set the minimum percentage of models that have a populated description.

Receives:

Name Type Description min_model_documentation_coverage_pct float

The minimum percentage of models that must have a populated description. Default: 100.

Example(s):

manifest_checks:\n    - name: check_model_description_populated\n      min_model_documentation_coverage_pct: 90\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@bouncer_check\ndef check_model_documentation_coverage(\n    request: TopRequest,\n    models: List[DbtBouncerModel],\n    min_model_documentation_coverage_pct: Union[float, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Set the minimum percentage of models that have a populated description.\n\n    Receives:\n        min_model_documentation_coverage_pct (float): The minimum percentage of models that must have a populated description. Default: 100.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_description_populated\n              min_model_documentation_coverage_pct: 90\n        ```\n    \"\"\"\n\n    num_models = len(models)\n    models_with_description = []\n    for model in models:\n        if len(model.description.strip()) > 4:\n            models_with_description.append(model.unique_id)\n\n    num_models_with_descriptions = len(models_with_description)\n    model_description_coverage_pct = (num_models_with_descriptions / num_models) * 100\n\n    assert (\n        model_description_coverage_pct >= min_model_documentation_coverage_pct  # type: ignore[operator]\n    ), f\"Only {model_description_coverage_pct}% of models have a populated description, this is less than the permitted minimum of {min_model_documentation_coverage_pct}%.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_documented_in_same_directory","title":"check_model_documented_in_same_directory","text":"

Models must be documented in the same directory where they are defined (i.e. .yml and .sql files are in the same directory).

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_documented_in_same_directory\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_documented_in_same_directory(\n    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Models must be documented in the same directory where they are defined (i.e. `.yml` and `.sql` files are in the same directory).\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_documented_in_same_directory\n        ```\n    \"\"\"\n\n    model_doc_dir = model.patch_path[model.patch_path.find(\"models\") :].split(\"/\")[1:-1]\n    model_sql_dir = model.path.split(\"/\")[:-1]\n\n    assert (\n        model_doc_dir == model_sql_dir\n    ), f\"`{model.name}` is documented in a different directory to the `.sql` file: `{'/'.join(model_doc_dir)}` vs `{'/'.join(model_sql_dir)}`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_code_does_not_contain_regexp_pattern","title":"check_model_code_does_not_contain_regexp_pattern","text":"

The raw code for a model must not match the specified regexp pattern.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

regexp_pattern str

The regexp pattern that should not be matched by the model code.

Example(s):

manifest_checks:\n    # Prefer `coalesce` over `ifnull`: https://docs.sqlfluff.com/en/stable/rules.html#sqlfluff.rules.sphinx.Rule_CV02\n    - name: check_model_code_does_not_contain_regexp_pattern\n      regexp_pattern: .*[i][f][n][u][l][l].*\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_code_does_not_contain_regexp_pattern(\n    request: TopRequest,\n    model: Union[DbtBouncerModel, None] = None,\n    regexp_pattern: Union[None, str] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    The raw code for a model must not match the specified regexp pattern.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n        regexp_pattern (str): The regexp pattern that should not be matched by the model code.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            # Prefer `coalesce` over `ifnull`: https://docs.sqlfluff.com/en/stable/rules.html#sqlfluff.rules.sphinx.Rule_CV02\n            - name: check_model_code_does_not_contain_regexp_pattern\n              regexp_pattern: .*[i][f][n][u][l][l].*\n        ```\n    \"\"\"\n\n    assert (\n        re.compile(regexp_pattern.strip(), flags=re.DOTALL).match(model.raw_code) is None\n    ), f\"`{model.name}` contains a banned string: `{regexp_pattern.strip()}`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_depends_on_multiple_sources","title":"check_model_depends_on_multiple_sources","text":"

Models cannot reference more than one source.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_depends_on_multiple_sources\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_depends_on_multiple_sources(\n    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Models cannot reference more than one source.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_depends_on_multiple_sources\n        ```\n    \"\"\"\n\n    num_reffed_sources = sum(x.split(\".\")[0] == \"source\" for x in model.depends_on.nodes)\n    assert num_reffed_sources <= 1, f\"`{model.name}` references more than one source.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_directories","title":"check_model_directories","text":"

Only specified sub-directories are permitted.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked. Special case: if you want to check ./models, pass \"\" to include.

model DbtBouncerModel

The DbtBouncerModel object to check.

permitted_sub_directories List[str]

List of permitted sub-directories.

Example(s):

manifest_checks:\n# Special case for top level directories within `./models`, pass \"\" to `include`\n- name: check_model_directories\n    include: \"\"\n    permitted_sub_directories:\n    - intermediate\n    - marts\n    - staging\n
# Restrict sub-directories within `./models/staging`\n- name: check_model_directories\n    include: ^staging\n    permitted_sub_directories:\n    - crm\n    - payments\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_directories(\n    request: TopRequest,\n    include: Union[None, str] = None,\n    model: Union[DbtBouncerModel, None] = None,\n    permitted_sub_directories: Union[List[str], None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Only specified sub-directories are permitted.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked. Special case: if you want to check `./models`, pass \"\" to `include`.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n        permitted_sub_directories (List[str]): List of permitted sub-directories.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n        # Special case for top level directories within `./models`, pass \"\" to `include`\n        - name: check_model_directories\n            include: \"\"\n            permitted_sub_directories:\n            - intermediate\n            - marts\n            - staging\n        ```\n        ```yaml\n        # Restrict sub-directories within `./models/staging`\n        - name: check_model_directories\n            include: ^staging\n            permitted_sub_directories:\n            - crm\n            - payments\n        ```\n    \"\"\"\n\n    # Special case for `models` directory\n    if include == \"\":\n        assert (\n            model.path.split(\"/\")[0] in permitted_sub_directories  # type: ignore[operator]\n        ), f\"`{model.name}` is located in `{model.path.split('/')[0]}`, this is not a valid sub-directory.\"\n    else:\n        matched_path = re.compile(include.strip()).match(model.path)\n        path_after_match = model.path[matched_path.end() + 1 :]  # type: ignore[union-attr]\n\n        assert (\n            path_after_match.split(\"/\")[0] in permitted_sub_directories  # type: ignore[operator]\n        ), f\"`{model.name}` is located in `{model.path.split('/')[0]}`, this is not a valid sub-directory.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_has_contracts_enforced","title":"check_model_has_contracts_enforced","text":"

Model must have contracts enforced.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_has_contracts_enforced\n        include: ^marts\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_has_contracts_enforced(\n    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Model must have contracts enforced.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_has_contracts_enforced\n                include: ^marts\n        ```\n    \"\"\"\n\n    assert model.contract.enforced is True, f\"`{model.name}` does not have contracts enforced.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_has_meta_keys","title":"check_model_has_meta_keys","text":"

The meta config for models must have the specified keys.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

keys NestedDict

A list (that may contain sub-lists) of required keys.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_has_meta_keys\n      keys:\n        - maturity\n        - owner\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_has_meta_keys(\n    request: TopRequest,\n    keys: Union[NestedDict, None] = None,\n    model: Union[DbtBouncerModel, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    The `meta` config for models must have the specified keys.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        keys (NestedDict): A list (that may contain sub-lists) of required keys.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_has_meta_keys\n              keys:\n                - maturity\n                - owner\n        ```\n    \"\"\"\n\n    missing_keys = find_missing_meta_keys(\n        meta_config=model.meta,\n        required_keys=keys.model_dump(),\n    )\n    assert (\n        missing_keys == []\n    ), f\"`{model.name}` is missing the following keys from the `meta` config: {[x.replace('>>', '') for x in missing_keys]}\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_has_no_upstream_dependencies","title":"check_model_has_no_upstream_dependencies","text":"

Identify if models have no upstream dependencies as this likely indicates hard-coded tables references.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_has_no_upstream_dependencies\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_has_no_upstream_dependencies(\n    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Identify if models have no upstream dependencies as this likely indicates hard-coded tables references.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_has_no_upstream_dependencies\n        ```\n    \"\"\"\n\n    assert (\n        len(model.depends_on.nodes) > 0\n    ), f\"`{model.name}` has no upstream dependencies, this likely indicates hard-coded tables references.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_has_tags","title":"check_model_has_tags","text":"

Models must have the specified tags.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

tags List[str]

List of tags to check for.

Example(s):

manifest_checks:\n    - name: check_model_has_tags\n      tags:\n        - tag_1\n        - tag_2\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_has_tags(\n    request: TopRequest,\n    model: Union[DbtBouncerModel, None] = None,\n    tags: Union[List[str], None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Models must have the specified tags.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n        tags (List[str]): List of tags to check for.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_has_tags\n              tags:\n                - tag_1\n                - tag_2\n        ```\n    \"\"\"\n\n    missing_tags = [tag for tag in tags if tag not in model.tags]\n    assert not missing_tags, f\"`{model.name}` is missing required tags: {missing_tags}.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_has_unique_test","title":"check_model_has_unique_test","text":"

Models must have a test for uniqueness of a column.

Receives:

Name Type Description accepted_uniqueness_tests Optional[List[str]]

List of tests that are accepted as uniqueness tests. If not provided, defaults to expect_compound_columns_to_be_unique, dbt_utils.unique_combination_of_columns and unique.

exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n  - name: check_model_has_unique_test\n    include: ^fct_|^dim_\n
manifest_checks:\n  # Example of allowing a custom uniqueness test\n  - name: check_model_has_unique_test\n    accepted_uniqueness_tests:\n        - expect_compound_columns_to_be_unique\n        - my_custom_uniqueness_test\n        - unique\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_has_unique_test(\n    request: TopRequest,\n    tests: List[DbtBouncerModel],\n    accepted_uniqueness_tests: Union[List[str], None] = None,\n    model: Union[DbtBouncerModel, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Models must have a test for uniqueness of a column.\n\n    Receives:\n        accepted_uniqueness_tests (Optional[List[str]]): List of tests that are accepted as uniqueness tests. If not provided, defaults to `expect_compound_columns_to_be_unique`, `dbt_utils.unique_combination_of_columns` and `unique`.\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n          - name: check_model_has_unique_test\n            include: ^fct_|^dim_\n        ```\n        ```yaml\n        manifest_checks:\n          # Example of allowing a custom uniqueness test\n          - name: check_model_has_unique_test\n            accepted_uniqueness_tests:\n                - expect_compound_columns_to_be_unique\n                - my_custom_uniqueness_test\n                - unique\n        ```\n    \"\"\"\n\n    num_unique_tests = sum(\n        test.attached_node == model.unique_id\n        and test.test_metadata.name in accepted_uniqueness_tests  # type: ignore[operator]\n        for test in tests\n    )\n    assert (\n        num_unique_tests >= 1\n    ), f\"`{model.name}` does not have a test for uniqueness of a column.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_has_unit_tests","title":"check_model_has_unit_tests","text":"

Models must have more than the specified number of unit tests.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

min_number_of_unit_tests Optional[int]

The minimum number of unit tests that a model must have. Default: 1.

model DbtBouncerModel

The DbtBouncerModel object to check.

Warning

This check is only supported for dbt 1.8.0 and above.

Example(s):

manifest_checks:\n    - name: check_model_has_unit_tests\n      include: ^marts\n
manifest_checks:\n    - name: check_model_has_unit_tests\n      min_number_of_unit_tests: 2\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_has_unit_tests(\n    manifest_obj: DbtBouncerManifest,\n    request: TopRequest,\n    unit_tests: List[UnitTests],\n    min_number_of_unit_tests: Union[int, None] = None,\n    model: Union[DbtBouncerModel, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Models must have more than the specified number of unit tests.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        min_number_of_unit_tests (Optional[int]): The minimum number of unit tests that a model must have. Default: 1.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    !!! warning\n\n        This check is only supported for dbt 1.8.0 and above.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_has_unit_tests\n              include: ^marts\n        ```\n        ```yaml\n        manifest_checks:\n            - name: check_model_has_unit_tests\n              min_number_of_unit_tests: 2\n        ```\n    \"\"\"\n\n    if semver.Version.parse(manifest_obj.manifest.metadata.dbt_version) >= \"1.8.0\":\n        num_unit_tests = len(\n            [t.unique_id for t in unit_tests if t.depends_on.nodes[0] == model.unique_id]\n        )\n        assert (\n            num_unit_tests >= min_number_of_unit_tests  # type: ignore[operator]\n        ), f\"`{model.name}` has {num_unit_tests} unit tests, this is less than the minimum of {min_number_of_unit_tests}.\"\n    else:\n        logging.warning(\n            \"The `check_model_has_unit_tests` check is only supported for dbt 1.8.0 and above.\"\n        )\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_max_chained_views","title":"check_model_max_chained_views","text":"

Models cannot have more than the specified number of upstream dependents that are not tables (default: 3).

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

materializations_to_include Optional[List[str]]

List of materializations to include in the check. If not provided, defaults to ephemeral and view.

max_chained_views Optional[int]

The maximum number of upstream dependents that are not tables. Default: 3

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_max_chained_views\n
manifest_checks:\n    - name: check_model_max_chained_views\n      materializations_to_include:\n        - ephemeral\n        - my_custom_materialization\n        - view\n      max_chained_views: 5\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_max_chained_views(\n    manifest_obj: DbtBouncerManifest,\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    materializations_to_include: Union[List[str], None] = None,\n    max_chained_views: Union[int, None] = None,\n    model: Union[DbtBouncerModel, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Models cannot have more than the specified number of upstream dependents that are not tables (default: 3).\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        materializations_to_include (Optional[List[str]]): List of materializations to include in the check. If not provided, defaults to `ephemeral` and `view`.\n        max_chained_views (Optional[int]): The maximum number of upstream dependents that are not tables. Default: 3\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_max_chained_views\n        ```\n        ```yaml\n        manifest_checks:\n            - name: check_model_max_chained_views\n              materializations_to_include:\n                - ephemeral\n                - my_custom_materialization\n                - view\n              max_chained_views: 5\n        ```\n    \"\"\"\n\n    def return_upstream_view_models(\n        materializations,\n        max_chained_views,\n        models,\n        model_unique_ids_to_check,\n        package_name,\n        depth=0,\n    ):\n        \"\"\"\n        Recursive function to return model unique_id's of upstream models that are views. Depth of recursion can be specified. If no models meet the criteria then an empty list is returned.\n        \"\"\"\n\n        if depth == max_chained_views or model_unique_ids_to_check == []:\n            return model_unique_ids_to_check\n\n        relevant_upstream_models = []\n        for model in model_unique_ids_to_check:\n            upstream_nodes = list(\n                [m2 for m2 in models if m2.unique_id == model][0].depends_on.nodes\n            )\n            if upstream_nodes != []:\n                upstream_models = [\n                    m\n                    for m in upstream_nodes\n                    if m.split(\".\")[0] == \"model\" and m.split(\".\")[1] == package_name\n                ]\n                for i in upstream_models:\n                    if [m for m in models if m.unique_id == i][\n                        0\n                    ].config.materialized in materializations:\n                        relevant_upstream_models.append(i)\n\n        depth += 1\n        return return_upstream_view_models(\n            materializations=materializations,\n            max_chained_views=max_chained_views,\n            models=models,\n            model_unique_ids_to_check=relevant_upstream_models,\n            package_name=package_name,\n            depth=depth,\n        )\n\n    assert (\n        len(\n            return_upstream_view_models(\n                materializations=materializations_to_include,\n                max_chained_views=max_chained_views,\n                models=models,\n                model_unique_ids_to_check=[model.unique_id],\n                package_name=manifest_obj.manifest.metadata.project_name,\n            )\n        )\n        == 0\n    ), f\"`{model.name}` has more than {max_chained_views} upstream dependents that are not tables.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_max_fanout","title":"check_model_max_fanout","text":"

Models cannot have more than the specified number of downstream models (default: 3).

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

max_downstream_models Optional[int]

The maximum number of permitted downstream models. Default: 3

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_max_fanout\n      max_downstream_models: 2\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_max_fanout(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    max_downstream_models: Union[int, None] = None,\n    model: Union[DbtBouncerModel, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Models cannot have more than the specified number of downstream models (default: 3).\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        max_downstream_models (Optional[int]): The maximum number of permitted downstream models. Default: 3\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_max_fanout\n              max_downstream_models: 2\n        ```\n    \"\"\"\n\n    num_downstream_models = sum(model.unique_id in m.depends_on.nodes for m in models)\n\n    assert (\n        num_downstream_models <= max_downstream_models  # type: ignore[operator]\n    ), f\"`{model.name}` has {num_downstream_models} downstream models, which is more than the permitted maximum of {max_downstream_models}.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_max_number_of_lines","title":"check_model_max_number_of_lines","text":"

Models may not have more than the specified number of lines.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern

None

will be checked.

max_number_of_lines int

The maximum number of permitted lines. Default: 100.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_max_number_of_lines\n
manifest_checks:\n    - name: check_model_max_number_of_lines\n      max_number_of_lines: 150\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_max_number_of_lines(\n    request: TopRequest,\n    max_number_of_lines: Union[int, None] = None,\n    model: Union[DbtBouncerModel, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Models may not have more than the specified number of lines.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern\n        will be checked.\n        max_number_of_lines (int): The maximum number of permitted lines. Default: 100.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_max_number_of_lines\n        ```\n        ```yaml\n        manifest_checks:\n            - name: check_model_max_number_of_lines\n              max_number_of_lines: 150\n        ```\n    \"\"\"\n\n    actual_number_of_lines = model.raw_code.count(\"\\n\") + 1\n\n    assert (\n        actual_number_of_lines <= max_number_of_lines\n    ), f\"`{model.name}` has {actual_number_of_lines} lines, this is more than the maximum permitted number of lines ({max_number_of_lines}).\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_max_upstream_dependencies","title":"check_model_max_upstream_dependencies","text":"

Limit the number of upstream dependencies a model has. Default values are 5 for models, 5 for macros, and 1 for sources.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

max_upstream_macros Optional[int]

The maximum number of permitted upstream macros. Default: 5

max_upstream_models Optional[int]

The maximum number of permitted upstream models. Default: 5

max_upstream_sources Optional[int]

The maximum number of permitted upstream sources. Default: 1

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_max_upstream_dependencies\n      max_upstream_models: 3\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_max_upstream_dependencies(\n    request: TopRequest,\n    max_upstream_macros: Union[int, None] = None,\n    max_upstream_models: Union[int, None] = None,\n    max_upstream_sources: Union[int, None] = None,\n    model: Union[DbtBouncerModel, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Limit the number of upstream dependencies a model has. Default values are 5 for models, 5 for macros, and 1 for sources.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        max_upstream_macros (Optional[int]): The maximum number of permitted upstream macros. Default: 5\n        max_upstream_models (Optional[int]): The maximum number of permitted upstream models. Default: 5\n        max_upstream_sources (Optional[int]): The maximum number of permitted upstream sources. Default: 1\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_max_upstream_dependencies\n              max_upstream_models: 3\n        ```\n    \"\"\"\n\n    num_upstream_macros = len(list(model.depends_on.macros))\n    num_upstream_models = len([m for m in model.depends_on.nodes if m.split(\".\")[0] == \"model\"])\n    num_upstream_sources = len([m for m in model.depends_on.nodes if m.split(\".\")[0] == \"source\"])\n\n    assert (\n        num_upstream_macros <= max_upstream_macros  # type: ignore[operator]\n    ), f\"`{model.name}` has {num_upstream_macros} upstream macros, which is more than the permitted maximum of {max_upstream_macros}.\"\n    assert (\n        num_upstream_models <= max_upstream_models  # type: ignore[operator]\n    ), f\"`{model.name}` has {num_upstream_models} upstream models, which is more than the permitted maximum of {max_upstream_models}.\"\n    assert (\n        num_upstream_sources <= max_upstream_sources  # type: ignore[operator]\n    ), f\"`{model.name}` has {num_upstream_sources} upstream sources, which is more than the permitted maximum of {max_upstream_sources}.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_names","title":"check_model_names","text":"

Models must have a name that matches the supplied regex.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model_name_pattern str

Regexp the model name must match.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_names\n      include: ^intermediate\n      model_name_pattern: ^int_\n    - name: check_model_names\n      include: ^staging\n      model_name_pattern: ^stg_\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_names(\n    request: TopRequest,\n    model: Union[DbtBouncerModel, None] = None,\n    model_name_pattern: Union[None, str] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Models must have a name that matches the supplied regex.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model_name_pattern (str): Regexp the model name must match.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_names\n              include: ^intermediate\n              model_name_pattern: ^int_\n            - name: check_model_names\n              include: ^staging\n              model_name_pattern: ^stg_\n        ```\n    \"\"\"\n\n    assert (\n        re.compile(model_name_pattern.strip()).match(model.name) is not None\n    ), f\"`{model.name}` does not match the supplied regex `{model_name_pattern.strip()})`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_property_file_location","title":"check_model_property_file_location","text":"

Model properties files must follow the guidance provided by dbt here.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_property_file_location\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_property_file_location(\n    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Model properties files must follow the guidance provided by dbt [here](https://docs.getdbt.com/best-practices/how-we-structure/1-guide-overview).\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_property_file_location\n        ```\n    \"\"\"\n\n    expected_substr = (\n        \"_\".join(model.path.split(\"/\")[:-1])\n        .replace(\"staging\", \"stg\")\n        .replace(\"intermediate\", \"int\")\n        .replace(\"marts\", \"\")\n    )\n    properties_yml_name = model.patch_path.split(\"/\")[-1]\n\n    assert properties_yml_name.startswith(\n        \"_\"\n    ), f\"The properties file for `{model.name}` (`{properties_yml_name}`) does not start with an underscore.\"\n    assert (\n        expected_substr in properties_yml_name\n    ), f\"The properties file for `{model.name}` (`{properties_yml_name}`) does not contain the expected substring (`{expected_substr}`).\"\n    assert properties_yml_name.endswith(\n        \"__models.yml\"\n    ), f\"The properties file for `{model.name}` (`{properties_yml_name}`) does not end with `__models.yml`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_test_coverage","title":"check_model_test_coverage","text":"

Set the minimum percentage of models that have at least one test.

Receives:

Name Type Description min_model_test_coverage_pct float

The minimum percentage of models that must have at least one test. Default: 100

Example(s):

manifest_checks:\n    - name: check_model_test_coverage\n      min_model_test_coverage_pct: 90\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@bouncer_check\ndef check_model_test_coverage(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    tests: List[DbtBouncerModel],\n    min_model_test_coverage_pct: Union[float, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Set the minimum percentage of models that have at least one test.\n\n    Receives:\n        min_model_test_coverage_pct (float): The minimum percentage of models that must have at least one test. Default: 100\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_test_coverage\n              min_model_test_coverage_pct: 90\n        ```\n    \"\"\"\n\n    num_models = len(models)\n    models_with_tests = []\n    for model in models:\n        for test in tests:\n            if model.unique_id in test.depends_on.nodes:\n                models_with_tests.append(model.unique_id)\n    num_models_with_tests = len(set(models_with_tests))\n    model_test_coverage_pct = (num_models_with_tests / num_models) * 100\n\n    assert (\n        model_test_coverage_pct >= min_model_test_coverage_pct  # type: ignore[operator]\n    ), f\"Only {model_test_coverage_pct}% of models have at least one test, this is less than the permitted minimum of {min_model_test_coverage_pct}%.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources","title":"check_sources","text":""},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_description_populated","title":"check_source_description_populated","text":"

Sources must have a populated description.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

source DbtBouncerSource

The DbtBouncerSource object to check.

Example(s):

manifest_checks:\n    - name: check_source_description_populated\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_description_populated(\n    request: TopRequest, source: Union[DbtBouncerSource, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Sources must have a populated description.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_description_populated\n        ```\n    \"\"\"\n\n    assert (\n        len(source.description.strip()) > 4\n    ), f\"`{source.source_name}.{source.name}` does not have a populated description.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_freshness_populated","title":"check_source_freshness_populated","text":"

Sources must have a populated freshness.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

source DbtBouncerSource

The DbtBouncerSource object to check.

Example(s):

manifest_checks:\n    - name: check_source_freshness_populated\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_freshness_populated(\n    request: TopRequest, source: Union[DbtBouncerSource, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Sources must have a populated freshness.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_freshness_populated\n        ```\n    \"\"\"\n\n    assert (\n        source.freshness.error_after.count is not None\n        and source.freshness.error_after.period is not None\n    ) or (\n        source.freshness.warn_after.count is not None\n        and source.freshness.warn_after.period is not None\n    ), f\"`{source.source_name}.{source.name}` does not have a populated freshness.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_has_meta_keys","title":"check_source_has_meta_keys","text":"

The meta config for sources must have the specified keys.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

keys NestedDict

A list (that may contain sub-lists) of required keys.

source DbtBouncerSource

The DbtBouncerSource object to check.

Example(s):

manifest_checks:\n    - name: check_source_has_meta_keys\n      keys:\n        - contact:\n            - email\n            - slack\n        - owner\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_has_meta_keys(\n    request,\n    keys: Union[NestedDict, None] = None,\n    source: Union[DbtBouncerSource, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    The `meta` config for sources must have the specified keys.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        keys (NestedDict): A list (that may contain sub-lists) of required keys.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_has_meta_keys\n              keys:\n                - contact:\n                    - email\n                    - slack\n                - owner\n        ```\n    \"\"\"\n\n    missing_keys = find_missing_meta_keys(\n        meta_config=source.meta,\n        required_keys=keys.model_dump(),\n    )\n    assert (\n        missing_keys == []\n    ), f\"`{source.source_name}.{source.name}` is missing the following keys from the `meta` config: {[x.replace('>>', '') for x in missing_keys]}\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_has_tags","title":"check_source_has_tags","text":"

Sources must have the specified tags.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

source DbtBouncerSource

The DbtBouncerSource object to check.

tags List[str]

List of tags to check for.

Example(s):

manifest_checks:\n    - name: check_source_has_tags\n      tags:\n        - tag_1\n        - tag_2\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_has_tags(\n    request: TopRequest,\n    source: Union[DbtBouncerSource, None] = None,\n    tags: Union[None, str] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Sources must have the specified tags.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n        tags (List[str]): List of tags to check for.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_has_tags\n              tags:\n                - tag_1\n                - tag_2\n        ```\n    \"\"\"\n\n    missing_tags = [tag for tag in tags if tag not in source.tags]\n    assert (\n        not missing_tags\n    ), f\"`{source.source_name}.{source.name}` is missing required tags: {missing_tags}.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_loader_populated","title":"check_source_loader_populated","text":"

Sources must have a populated loader.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

source DbtBouncerSource

The DbtBouncerSource object to check.

Example(s):

manifest_checks:\n    - name: check_source_loader_populated\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_loader_populated(\n    request: TopRequest, source: Union[DbtBouncerSource, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Sources must have a populated loader.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_loader_populated\n        ```\n    \"\"\"\n\n    assert (\n        source.loader != \"\"\n    ), f\"`{source.source_name}.{source.name}` does not have a populated loader.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_names","title":"check_source_names","text":"

Sources must have a name that matches the supplied regex.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

source DbtBouncerSource

The DbtBouncerSource object to check.

source_name_pattern str

Regexp the source name must match.

Example(s):

manifest_checks:\n    - name: check_source_names\n      source_name_pattern: >\n        ^[a-z0-9_]*$\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_names(\n    request: TopRequest,\n    source: Union[DbtBouncerSource, None] = None,\n    source_name_pattern: Union[None, str] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Sources must have a name that matches the supplied regex.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n        source_name_pattern (str): Regexp the source name must match.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_names\n              source_name_pattern: >\n                ^[a-z0-9_]*$\n        ```\n    \"\"\"\n\n    assert (\n        re.compile(source_name_pattern.strip()).match(source.name) is not None\n    ), f\"`{source.source_name}.{source.name}` does not match the supplied regex `({source_name_pattern.strip()})`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_not_orphaned","title":"check_source_not_orphaned","text":"

Sources must be referenced in at least one model.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

source DbtBouncerSource

The DbtBouncerSource object to check.

Example(s):

manifest_checks:\n    - name: check_source_not_orphaned\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_not_orphaned(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    source: Union[DbtBouncerSource, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Sources must be referenced in at least one model.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_not_orphaned\n        ```\n    \"\"\"\n\n    num_refs = sum(source.unique_id in model.depends_on.nodes for model in models)\n    assert (\n        num_refs >= 1\n    ), f\"Source `{source.source_name}.{source.name}` is orphaned, i.e. not referenced by any model.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_property_file_location","title":"check_source_property_file_location","text":"

Source properties files must follow the guidance provided by dbt here.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

source DbtBouncerSource

The DbtBouncerSource object to check.

Example(s):

manifest_checks:\n    - name: check_source_property_file_location\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_property_file_location(\n    request: TopRequest, source: Union[DbtBouncerSource, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Source properties files must follow the guidance provided by dbt [here](https://docs.getdbt.com/best-practices/how-we-structure/1-guide-overview).\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_property_file_location\n        ```\n    \"\"\"\n\n    path_cleaned = source.path.replace(\"models/staging\", \"\")\n    expected_substring = \"_\".join(path_cleaned.split(\"/\")[:-1])\n\n    assert path_cleaned.split(\"/\")[-1].startswith(\n        \"_\"\n    ), f\"The properties file for `{source.source_name}.{source.name}` (`{path_cleaned}`) does not start with an underscore.\"\n    assert (\n        expected_substring in path_cleaned\n    ), f\"The properties file for `{source.source_name}.{source.name}` (`{path_cleaned}`) does not contain the expected substring (`{expected_substring}`).\"\n    assert path_cleaned.split(\"/\")[-1].endswith(\n        \"__sources.yml\"\n    ), f\"The properties file for `{source.source_name}.{source.name}` (`{path_cleaned}`) does not end with `__sources.yml`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_used_by_models_in_same_directory","title":"check_source_used_by_models_in_same_directory","text":"

Sources can only be referenced by models that are located in the same directory where the source is defined.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

source DbtBouncerSource

The DbtBouncerSource object to check.

Example(s):

manifest_checks:\n    - name: check_source_used_by_models_in_same_directory\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_used_by_models_in_same_directory(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    source: Union[DbtBouncerSource, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Sources can only be referenced by models that are located in the same directory where the source is defined.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_used_by_models_in_same_directory\n        ```\n    \"\"\"\n\n    reffed_models_not_in_same_dir = []\n    for model in models:\n        if (\n            source.unique_id in model.depends_on.nodes\n            and model.path.split(\"/\")[:-1] != source.path.split(\"/\")[1:-1]\n        ):\n            reffed_models_not_in_same_dir.append(model.unique_id.split(\".\")[0])\n\n    assert (\n        len(reffed_models_not_in_same_dir) == 0\n    ), f\"Source `{source.source_name}.{source.name}` is referenced by models defined in a different directory: {reffed_models_not_in_same_dir}\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_used_by_only_one_model","title":"check_source_used_by_only_one_model","text":"

Each source can be referenced by a maximum of one model.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

source DbtBouncerSource

The DbtBouncerSource object to check.

Example(s):

manifest_checks:\n    - name: check_source_used_by_only_one_model\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_used_by_only_one_model(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    source: Union[DbtBouncerSource, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Each source can be referenced by a maximum of one model.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_used_by_only_one_model\n        ```\n    \"\"\"\n\n    num_refs = sum(source.unique_id in model.depends_on.nodes for model in models)\n    assert (\n        num_refs <= 1\n    ), f\"Source `{source.source_name}.{source.name}` is referenced by more than one model.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_unit_tests","title":"check_unit_tests","text":""},{"location":"checks/checks_manifest/#manifest.check_unit_tests.check_unit_test_expect_format","title":"check_unit_test_expect_format","text":"

Unit tests can only use the specified formats.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the unit test path (i.e the .yml file where the unit test is configured). Unit test paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the unit test path (i.e the .yml file where the unit test is configured). Only unit test paths that match the pattern will be checked.

permitted_formats Optional[List[Literal['csv', 'dict', 'sql']]]

A list of formats that are allowed to be used for expect input in a unit test.

unit_test UnitTests

The UnitTests object to check.

Warning

This check is only supported for dbt 1.8.0 and above.

Example(s):

manifest_checks:\n    - name: check_unit_test_expect_format\n      permitted_formats:\n        - csv\n

Source code in src/dbt_bouncer/checks/manifest/check_unit_tests.py
@pytest.mark.iterate_over_unit_tests\n@bouncer_check\ndef check_unit_test_expect_format(\n    manifest_obj: DbtBouncerManifest,\n    request: TopRequest,\n    permitted_formats: Union[List[Literal[\"csv\", \"dict\", \"sql\"]], None] = None,\n    unit_test: Union[UnitTests, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Unit tests can only use the specified formats.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the unit test path (i.e the .yml file where the unit test is configured). Unit test paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the unit test path (i.e the .yml file where the unit test is configured). Only unit test paths that match the pattern will be checked.\n        permitted_formats (Optional[List[Literal[\"csv\", \"dict\", \"sql\"]]]): A list of formats that are allowed to be used for `expect` input in a unit test.\n        unit_test (UnitTests): The UnitTests object to check.\n\n    !!! warning\n\n        This check is only supported for dbt 1.8.0 and above.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_unit_test_expect_format\n              permitted_formats:\n                - csv\n        ```\n    \"\"\"\n\n    if semver.Version.parse(manifest_obj.manifest.metadata.dbt_version) >= \"1.8.0\":\n        assert (\n            unit_test.expect.format.value in permitted_formats  # type: ignore[operator]\n        ), f\"Unit test `{unit_test.name}` has an `expect` format that is not permitted. Permitted formats are: {permitted_formats}.\"\n    else:\n        logging.warning(\n            \"The `check_unit_test_expect_format` check is only supported for dbt 1.8.0 and above.\"\n        )\n
"},{"location":"checks/checks_manifest/#manifest.check_unit_tests.check_unit_test_given_formats","title":"check_unit_test_given_formats","text":"

Unit tests can only use the specified formats.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the unit test path (i.e the .yml file where the unit test is configured). Unit test paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the unit test path (i.e the .yml file where the unit test is configured). Only unit test paths that match the pattern will be checked.

permitted_formats Optional[List[Literal['csv', 'dict', 'sql']]]

A list of formats that are allowed to be used for given inputs in a unit test.

unit_test UnitTests

The UnitTests object to check.

Warning

This check is only supported for dbt 1.8.0 and above.

Example(s):

manifest_checks:\n    - name: check_unit_test_given_formats\n      permitted_formats:\n        - csv\n

Source code in src/dbt_bouncer/checks/manifest/check_unit_tests.py
@pytest.mark.iterate_over_unit_tests\n@bouncer_check\ndef check_unit_test_given_formats(\n    manifest_obj: DbtBouncerManifest,\n    request: TopRequest,\n    permitted_formats: Union[List[Literal[\"csv\", \"dict\", \"sql\"]], None] = None,\n    unit_test: Union[UnitTests, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Unit tests can only use the specified formats.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the unit test path (i.e the .yml file where the unit test is configured). Unit test paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the unit test path (i.e the .yml file where the unit test is configured). Only unit test paths that match the pattern will be checked.\n        permitted_formats (Optional[List[Literal[\"csv\", \"dict\", \"sql\"]]]): A list of formats that are allowed to be used for `given` inputs in a unit test.\n        unit_test (UnitTests): The UnitTests object to check.\n\n    !!! warning\n\n        This check is only supported for dbt 1.8.0 and above.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_unit_test_given_formats\n              permitted_formats:\n                - csv\n        ```\n    \"\"\"\n\n    if semver.Version.parse(manifest_obj.manifest.metadata.dbt_version) >= \"1.8.0\":\n        given_formats = [i.format.value for i in unit_test.given]\n        assert all(\n            e in permitted_formats for e in given_formats  # type: ignore[operator]\n        ), f\"Unit test `{unit_test.name}` has given formats which are not permitted. Permitted formats are: {permitted_formats}.\"\n    else:\n        logging.warning(\n            \"The `check_unit_test_given_formats` check is only supported for dbt 1.8.0 and above.\"\n        )\n
"},{"location":"checks/checks_run_results/","title":"Run Results Checks","text":"

Note

The below checks require both manifest.json and run_results.json to be present.

"},{"location":"checks/checks_run_results/#run_results.check_run_results","title":"check_run_results","text":""},{"location":"checks/checks_run_results/#run_results.check_run_results.check_run_results_max_gigabytes_billed","title":"check_run_results_max_gigabytes_billed","text":"

Each result can have a maximum number of gigabytes billed.

Note

Note that this check only works for the dbt-bigquery adapter.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the resource path. Resource paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the resource path. Only resource paths that match the pattern will be checked.

max_gigabytes_billed float

The maximum gigabytes billed allowed for a node.

run_result DbtBouncerResult

The DbtBouncerResult object to check.

Example(s):

run_results_checks:\n    - name: check_run_results_max_gigabytes_billed\n      max_gigabytes_billed: 100\n

Source code in src/dbt_bouncer/checks/run_results/check_run_results.py
@pytest.mark.iterate_over_run_results\n@bouncer_check\ndef check_run_results_max_gigabytes_billed(\n    request: TopRequest,\n    max_gigabytes_billed: Union[float, None] = None,\n    run_result: Union[DbtBouncerResult, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Each result can have a maximum number of gigabytes billed.\n\n    !!! note\n\n        Note that this check only works for the `dbt-bigquery` adapter.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the resource path. Resource paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the resource path. Only resource paths that match the pattern will be checked.\n        max_gigabytes_billed (float): The maximum gigabytes billed allowed for a node.\n        run_result (DbtBouncerResult): The DbtBouncerResult object to check.\n\n    Example(s):\n        ```yaml\n        run_results_checks:\n            - name: check_run_results_max_gigabytes_billed\n              max_gigabytes_billed: 100\n        ```\n    \"\"\"\n\n    try:\n        gigabytes_billed = run_result.adapter_response[\"bytes_billed\"] / (1000**3)\n    except KeyError:\n        raise RuntimeError(\n            \"`bytes_billed` not found in adapter response. Are you using the `dbt-bigquery` adapter?\"\n        )\n\n    assert (\n        gigabytes_billed < max_gigabytes_billed\n    ), f\"`{run_result.unique_id.split('.')[-2]}` results in ({gigabytes_billed} billed bytes, this is greater than permitted ({max_gigabytes_billed}).\"\n
"},{"location":"checks/checks_run_results/#run_results.check_run_results.check_run_results_max_execution_time","title":"check_run_results_max_execution_time","text":"

Each result can take a maximum duration (seconds).

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the resource path. Resource paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the resource path. Only resource paths that match the pattern will be checked.

max_execution_time_seconds float

The maximum execution time (seconds) allowed for a node.

run_result DbtBouncerResult

The DbtBouncerResult object to check.

Example(s):

run_results_checks:\n    - name: check_run_results_max_execution_time\n      max_execution_time_seconds: 60\n
run_results_checks:\n    - name: check_run_results_max_execution_time\n      include: ^staging # Not a good idea, here for demonstration purposes only\n      max_execution_time_seconds: 10\n

Source code in src/dbt_bouncer/checks/run_results/check_run_results.py
@pytest.mark.iterate_over_run_results\n@bouncer_check\ndef check_run_results_max_execution_time(\n    request: TopRequest,\n    max_execution_time_seconds: Union[float, None] = None,\n    run_result: Union[DbtBouncerResult, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Each result can take a maximum duration (seconds).\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the resource path. Resource paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the resource path. Only resource paths that match the pattern will be checked.\n        max_execution_time_seconds (float): The maximum execution time (seconds) allowed for a node.\n        run_result (DbtBouncerResult): The DbtBouncerResult object to check.\n\n    Example(s):\n        ```yaml\n        run_results_checks:\n            - name: check_run_results_max_execution_time\n              max_execution_time_seconds: 60\n        ```\n        ```yaml\n        run_results_checks:\n            - name: check_run_results_max_execution_time\n              include: ^staging # Not a good idea, here for demonstration purposes only\n              max_execution_time_seconds: 10\n        ```\n    \"\"\"\n\n    assert (\n        run_result.execution_time <= max_execution_time_seconds\n    ), f\"`{run_result.unique_id.split('.')[-1]}` has an execution time ({run_result.execution_time} greater than permitted ({max_execution_time_seconds}s).\"\n
"}]} \ No newline at end of file +{"config":{"lang":["en"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"]},"docs":[{"location":"","title":"Home","text":""},{"location":"#welcome-to-dbt-bouncer","title":"Welcome to dbt-bouncer","text":"

dbt-bouncer is an open-source tool that allows you to configure and enforce conventions for your dbt project. The conventions are run against dbt's artifact files (think ./target/manifest.json) resulting in speedy tests. Conventions can be specified in a .yml file, allowing maximum customisation to the conventions you wish to follow (or create \ud83d\ude00).

Check out our Getting Started guide.

"},{"location":"#terminology","title":"Terminology","text":"
  • Check: A check is a rule run against a dbt artifact.
  • Config file: A .yml file that specifies which checks to run along with any parameters.
  • dbt artifacts directory: The directory that contains the dbt artifacts (manifest.json, etc.), generally this is ./target.
"},{"location":"#about","title":"About","text":"

dbt-bouncer is free software, released under the MIT license. It originated at Xebia Data in Amsterdam, Netherlands. Source code is available on GitHub.

All contributions, in the form of bug reports, pull requests, feedback or discussion are welcome. See the contributing guide for more information.

"},{"location":"CONTRIBUTING/","title":"Contributing to dbt-bouncer","text":"

dbt-bouncer is open source software. Whether you are a seasoned open source contributor or a first-time committer, we welcome and encourage you to contribute code, documentation, ideas, or problem statements to this project.

"},{"location":"CONTRIBUTING/#about-this-document","title":"About this document","text":"

There are many ways to contribute to the ongoing development of dbt-bouncer, such as by participating in discussions and issues.

The rest of this document serves as a more granular guide for contributing code changes to dbt-bouncer (this repository). It is not intended as a guide for using dbt-bouncer, and some pieces assume a level of familiarity with Python development (virtualenvs, Poetry, etc). Specific code snippets in this guide assume you are using macOS or Linux and are comfortable with the command line.

If you get stuck, we're happy to help! Just open an issue or draft PR and we'll do our best to help out.

"},{"location":"CONTRIBUTING/#note","title":"Note","text":"
  • Branches: All pull requests from community contributors should target the main branch (default).
"},{"location":"CONTRIBUTING/#getting-the-code","title":"Getting the code","text":""},{"location":"CONTRIBUTING/#installing-git","title":"Installing git","text":"

You will need git in order to download and modify the dbt-bouncer source code. On macOS, the best way to download git is to just install Xcode.

"},{"location":"CONTRIBUTING/#contributors","title":"Contributors","text":"

You can contribute to dbt-bouncer by forking the dbt-bouncer repository. For a detailed overview on forking, check out the GitHub docs on forking. In short, you will need to:

  1. Fork the dbt-bouncer repository.
  2. Clone your fork locally.
  3. Check out a new branch for your proposed changes.
  4. Push changes to your fork.
  5. Open a pull request against godatadriven/dbt-bouncer from your forked repository.
"},{"location":"CONTRIBUTING/#setting-up-an-environment","title":"Setting up an environment","text":"

There are some tools that will be helpful to you in developing locally. While this is the list relevant for dbt-bouncer development, many of these tools are used commonly across open-source python projects.

"},{"location":"CONTRIBUTING/#tools","title":"Tools","text":"

These are the tools used in dbt-bouncer development and testing:

  • black for code formatting.
  • click to create our CLI interface.
  • GitHub Actions for automating tests and checks, once a PR is pushed to the dbt-bouncer repository.
  • make to run multiple setup or test steps in combination.
  • mypy for static type checking.
  • Poetry to manage our python virtual environment.
  • pre-commit to easily run those checks.
  • Pydantic to validate our configuration file.
  • pytest to define, discover, and run tests.

A deep understanding of these tools in not required to effectively contribute to dbt-bouncer, but we recommend checking out the attached documentation if you're interested in learning more about each one.

"},{"location":"CONTRIBUTING/#virtual-environments","title":"Virtual environments","text":"

We strongly recommend using virtual environments when developing code in dbt-bouncer. We recommend creating this virtualenv in the root of the dbt-bouncer repository. To create a new virtualenv, run:

poetry shell\n

This will create a new Python virtual environment.

"},{"location":"CONTRIBUTING/#setting-environment-variables","title":"Setting environment variables","text":"

Set required environment variables by copying .env.example to .env and updating the values.

"},{"location":"CONTRIBUTING/#running-dbt-bouncer-in-development","title":"Running dbt-bouncer in development","text":""},{"location":"CONTRIBUTING/#installation","title":"Installation","text":"

First make sure that you set up your virtualenv as described in Setting up an environment. Next, install dbt-bouncer, its dependencies and pre-commit:

poetry install\npoetry run pre-commit install\n

When installed in this way, any changes you make to your local copy of the source code will be reflected immediately in your next dbt-bouncer run.

"},{"location":"CONTRIBUTING/#running-dbt-bouncer","title":"Running dbt-bouncer","text":"

With your virtualenv activated, the dbt-bouncer script should point back to the source code you've cloned on your machine. You can verify this by running which dbt-bouncer. This command should show you a path to an executable in your virtualenv. You can run dbt-bouncer using the provided example configuration file via:

poetry run dbt-bouncer --config-file dbt-bouncer-example.yml\n
"},{"location":"CONTRIBUTING/#testing","title":"Testing","text":"

Once you're able to manually test that your code change is working as expected, it's important to run existing automated tests, as well as adding some new ones. These tests will ensure that: - Your code changes do not unexpectedly break other established functionality - Your code changes can handle all known edge cases - The functionality you're adding will keep working in the future

"},{"location":"CONTRIBUTING/#note_1","title":"Note","text":"
  • Generating dbt artifacts: If you change the configuration of the dbt project located in dbt_project then you will need to re-generate the dbt artifacts used in testing. To do so, run:
make build-artifacts\n
"},{"location":"CONTRIBUTING/#test-commands","title":"Test commands","text":"

There are a few methods for running tests locally.

"},{"location":"CONTRIBUTING/#makefile","title":"makefile","text":"

There are multiple targets in the makefile to run common test suites, most notably:

# Runs unit tests\nmake test-unit\n\n# Runs integration tests\nmake test-integration\n\n# Runs all tests\nmake test\n
"},{"location":"CONTRIBUTING/#pre-commit","title":"pre-commit","text":"

pre-commit takes care of running all code-checks for formatting and linting. Run poetry run pre-commit install to install pre-commit in your local environment. Once this is done you can use the git pre-commit hooks to ensure proper formatting and linting.

"},{"location":"CONTRIBUTING/#pytest","title":"pytest","text":"

Finally, you can also run a specific test or group of tests using pytest directly. With a virtualenv active and dev dependencies installed you can do things like:

# run all unit tests in a file\npoetry run pytest ./tests/unit/checks/catalog/test_columns.py\n\n# run a specific unit test\npoetry run pytest ./tests/unit/checks/catalog/test_columns.py::test_check_columns_are_documented_in_public_models\n

See pytest usage docs for an overview of useful command-line options.

"},{"location":"CONTRIBUTING/#assorted-development-tips","title":"Assorted development tips","text":"
  • Append # type: ignore to the end of a line if you need to disable mypy on that line.
"},{"location":"CONTRIBUTING/#submitting-a-pull-request","title":"Submitting a Pull Request","text":"

Code can be merged into the current development branch main by opening a pull request. If the proposal looks like it's on the right track, then a dbt-bouncer maintainer will review the PR. They may suggest code revision for style or clarity, or request that you add unit or integration test(s). These are good things! We believe that, with a little bit of help, anyone can contribute high-quality code. Once merged, your contribution will be available for the next release of dbt-bouncer.

Automated tests run via GitHub Actions. If you're a first-time contributor, all tests will require a maintainer to approve.

Once all tests are passing and your PR has been approved, a dbt-bouncer maintainer will merge your changes into the active development branch. And that's it! Happy developing :tada:

"},{"location":"cli/","title":"CLI","text":"

This page provides documentation for the dbt-bouncer CLI.

"},{"location":"cli/#dbt-bouncer","title":"dbt-bouncer","text":"

Usage:

dbt-bouncer [OPTIONS]\n

Options:

  --config-file PATH  Location of the YML config file.\n  --output-file PATH  Location of the json file where check metadata will be\n                      saved.\n  -v, --verbosity     Verbosity.\n  --version           Show the version and exit.\n  --help              Show this message and exit.\n
"},{"location":"cli/#exit-codes","title":"Exit codes","text":"

dbt-bouncer returns the following exit codes:

  • 0: All checks have succeeded.

  • 1:

    • At least one check has failed. Check the logs for more information.
    • A fatal error occurred during check setup or check execution. Check the logs for more information.
"},{"location":"config_file/","title":"Config file","text":"

dbt-bouncer requires a config file which determines what checks are run. The following options are available, in order of priority:

  1. A file passed via the --config-file CLI flag.
  2. A file named dbt-bouncer.yml in the current working directory.
  3. A [tool.dbt-bouncer] section in pyproject.toml.

Here is an example config file in yaml:

# [Optional] Directory where the dbt artifacts exists, generally the `target` directory inside a dbt project. Defaults to `./target`.\ndbt_artifacts_dir: target\n\nmanifest_checks:\n  - name: check_macro_name_matches_file_name\n  - name: check_model_names\n    include: ^models/staging\n    model_name_pattern: ^stg_\n

And the same config in toml:

[tool.dbt-bouncer]\n# [Optional] Directory where the dbt artifacts exists, generally the `target` directory inside a dbt project. Defaults to `./target`.\ndbt_artifacts_dir = \"target\"\n\n[[tool.dbt-bouncer.manifest_checks]]\nname = \"check_macro_name_matches_file_name\"\n\n[[tool.dbt-bouncer.manifest_checks]]\nname = \"check_model_names\"\ninclude = \"^models/staging\"\nmodel_name_pattern = \"^stg_\"\n

For more example config files, see here.

"},{"location":"config_file/#common-arguments","title":"Common arguments","text":"

Most (but not all) checks accept the following optional arguments:

  • exclude: Regexp to match which original file paths to exclude.
  • include: Regexp to match which original file paths to include.

Example per resource type:

  • Exposures: The original file path to the properties file where the source is defined, e.g. ^models/marts/finance will match exposures defined in ./models/marts/finance/_exposures.yml.
  • Macros: The original file path to the macro file, e.g. ^macros/system will match files like ./macros/system/generate_schema_name.sql.
  • Models: The original file path to the model file, e.g. ^marts will match files like ./models/marts/customers.sql.
  • Run results: The original file path to the file associated with the resource, e.g. ^seeds/finance will match seeds in ./seeds/finance, ^models/staging will match models and tests in ./models/staging.
  • Sources: The original file path to the properties file where the source is defined, e.g. ^models/staging/crm will match sources defined in ./models/staging/crm/_crm__sources.yml.
  • Unit tests: The original file path to the properties file where the unit test is defined, e.g. ^models/staging/crm will match unit tests defined in ^staging/crm/_stg_crm__unit_tests.yml.

To determine if a check accepts these arguments view the Checks page.

Note

exclude and include can be specified at both the check level and the global level. Should both levels be specified, then the check level is applied. All the below examples result in the check_model_names check being run on all models in ./models/staging:

# Specify `include` at the check level only\nmanifest_checks:\n  - name: check_model_names\n    include: ^models/staging\n    model_name_pattern: ^stg_\n
# Specify `include` at the check and global levels\ninclude: ^models/marts\nmanifest_checks:\n  - name: check_model_names\n    include: ^models/staging\n    model_name_pattern: ^stg_\n
# Specify `include` at the global level only\ninclude: ^models/staging\nmanifest_checks:\n  - name: check_model_names\n    model_name_pattern: ^stg_\n
"},{"location":"faq/","title":"FAQ","text":"How to set up `dbt-bouncer` in a monorepo? A monorepo may consist of one directory with a dbt project and other directories with unrelated code. It may be desired for `dbt-bouncer` to be configured from the root directory. Sample directory tree:
.\n\u251c\u2500\u2500 dbt-bouncer.yml\n\u251c\u2500\u2500 README.md\n\u251c\u2500\u2500 dbt-project\n\u2502   \u251c\u2500\u2500 models\n\u2502   \u251c\u2500\u2500 dbt_project.yml\n\u2502   \u2514\u2500\u2500 profiles.yml\n\u2514\u2500\u2500 package-a\n    \u251c\u2500\u2500 src\n    \u251c\u2500\u2500 tests\n    \u2514\u2500\u2500 package.json\n
To ease configuration you can use `exclude` or `include` at the global level (see [Config File](./config-file.md) for more details). For the above example `dbt-bouncer.yml` could be configured as:
dbt_artifacts_dir: dbt-project/target\ninclude: ^dbt-project\n\nmanifest_checks:\n    - name: check_exposure_based_on_non_public_models\n
`dbt-bouncer` can now be run from the root directory."},{"location":"getting_started/","title":"Getting Started","text":""},{"location":"getting_started/#how-to-run-dbt-bouncer","title":"How to run dbt-bouncer","text":"
  1. Generate dbt artifacts by running a dbt command:

    • dbt parse to generate a manifest.json artifact.
    • dbt docs generate to generate a catalog.json artifact (necessary if you are using catalog checks).
    • dbt run (or any other command that implies it e.g. dbt build) to generate a run_results.json artifact (necessary if you are using run results checks).
  2. Create a dbt-bouncer.yml config file, details here.

  3. Run dbt-bouncer to validate that your conventions are being maintained.

"},{"location":"getting_started/#python","title":"Python","text":"

Install from pypi.org:

pip install dbt-bouncer\n

Run:

dbt-bouncer --config-file <PATH_TO_CONFIG_FILE>\n
Running dbt-bouncer (X.X.X)...\nLoaded config from dbt-bouncer-example.yml...\nValidating conf...\n

dbt-bouncer also supports a verbose mode, run:

dbt-bouncer --config-file <PATH_TO_CONFIG_FILE> -v\n
Running dbt-bouncer (X.X.X)...\nconfig_file=PosixPath('dbt-bouncer-example.yml')\nconfig_file_source='COMMANDLINE'\nConfig file passed via command line: dbt-bouncer-example.yml\nLoading config from /home/pslattery/repos/dbt-bouncer/dbt-bouncer-example.yml...\nLoading config from dbt-bouncer-example.yml...\nLoaded config from dbt-bouncer-example.yml...\nconf={'dbt_artifacts_dir': 'dbt_project/target', 'catalog_checks': [{'name': 'check_column_name_complies_to_column_type', 'column_name_pattern': '^is_.*', 'exclude': '^staging', 'types': ['BOOLEAN']}]}\nValidating conf...\n
"},{"location":"getting_started/#github-actions","title":"GitHub Actions","text":"

Run dbt-bouncer as part of your CI pipeline:

steps:\n    ...\n\n    - uses: godatadriven/dbt-bouncer@vX.X\n      with:\n        config-file: ./<PATH_TO_CONFIG_FILE>\n        output-file: results.json # optional, default does not save a results file\n        send-pr-comment: true # optional, defaults to true\n        verbose: false # optional, defaults to false\n\n    ...\n

We recommend pinning both a major and minor version number.

"},{"location":"getting_started/#docker","title":"Docker","text":"

Run dbt-bouncer via Docker:

docker run --rm \\\n    --volume \"$PWD\":/app \\\n    ghcr.io/godatadriven/dbt-bouncer:vX.X.X \\\n    --config-file /app/<PATH_TO_CONFIG_FILE>\n
"},{"location":"getting_started/#pex","title":"Pex","text":"

You can also run the .pex (Python EXecutable) artifact directly once you have a python executable (3.8 -> 3.12) installed:

wget https://github.com/godatadriven/dbt-bouncer/releases/download/vX.X.X/dbt-bouncer.pex -O dbt-bouncer.pex\n\npython dbt-bouncer.pex --config-file $PWD/<PATH_TO_CONFIG_FILE>\n
"},{"location":"checks/","title":"Checks","text":"

dbt-bouncer runs checks against artifacts from dbt. These checks fall into three categories:

  • Manifest Checks
  • Catalog Checks
  • Run Results Checks
"},{"location":"checks/checks_catalog/","title":"Catalog Checks","text":"

Note

The below checks require both catalog.json and manifest.json to be present.

"},{"location":"checks/checks_catalog/#catalog.check_catalog_sources","title":"check_catalog_sources","text":""},{"location":"checks/checks_catalog/#catalog.check_catalog_sources.check_source_columns_are_all_documented","title":"check_source_columns_are_all_documented","text":"

All columns in a source should be included in the source's properties file, i.e. .yml file.

Receives:

Name Type Description catalog_source DbtBouncerCatalogNode

The DbtBouncerCatalogNode object to check.

exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

Example(s):

catalog_checks:\n    - name: check_source_columns_are_all_documented\n

Source code in src/dbt_bouncer/checks/catalog/check_catalog_sources.py
@pytest.mark.iterate_over_catalog_sources\n@bouncer_check\ndef check_source_columns_are_all_documented(\n    sources: List[DbtBouncerSource],\n    request: TopRequest,\n    catalog_source: Union[DbtBouncerCatalogNode, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    All columns in a source should be included in the source's properties file, i.e. `.yml` file.\n\n    Receives:\n        catalog_source (DbtBouncerCatalogNode): The DbtBouncerCatalogNode object to check.\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n\n    Example(s):\n        ```yaml\n        catalog_checks:\n            - name: check_source_columns_are_all_documented\n        ```\n    \"\"\"\n\n    source = [s for s in sources if s.unique_id == catalog_source.unique_id][0]\n    undocumented_columns = [\n        v.name for _, v in catalog_source.columns.items() if v.name not in source.columns.keys()\n    ]\n    assert (\n        not undocumented_columns\n    ), f\"`{catalog_source.unique_id}` has columns that are not included in the sources properties file: {undocumented_columns}\"\n
"},{"location":"checks/checks_catalog/#catalog.check_columns","title":"check_columns","text":""},{"location":"checks/checks_catalog/#catalog.check_columns.check_column_description_populated","title":"check_column_description_populated","text":"

Columns must have a populated description.

Receives:

Name Type Description catalog_node CatalogTable

The CatalogTable object to check.

exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

Example(s):

manifest_checks:\n    - name: check_column_description_populated\n      include: ^models/marts\n

Source code in src/dbt_bouncer/checks/catalog/check_columns.py
@pytest.mark.iterate_over_catalog_nodes\n@bouncer_check\ndef check_column_description_populated(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    catalog_node: Union[CatalogTable, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Columns must have a populated description.\n\n    Receives:\n        catalog_node (CatalogTable): The CatalogTable object to check.\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_column_description_populated\n              include: ^models/marts\n        ```\n    \"\"\"\n\n    if catalog_node.unique_id.split(\".\")[0] == \"model\":\n        model = [m for m in models if m.unique_id == catalog_node.unique_id][0]\n        non_complying_columns = []\n        for _, v in catalog_node.columns.items():\n            if (\n                model.columns.get(v.name) is None\n                or len(model.columns[v.name].description.strip()) <= 4\n            ):\n                non_complying_columns.append(v.name)\n\n        assert (\n            not non_complying_columns\n        ), f\"`{catalog_node.unique_id.split('.')[-1]}` has columns that do not have a populated description: {non_complying_columns}\"\n
"},{"location":"checks/checks_catalog/#catalog.check_columns.check_column_name_complies_to_column_type","title":"check_column_name_complies_to_column_type","text":"

Columns with specified data types must comply to the specified regexp naming pattern.

Receives:

Name Type Description catalog_node CatalogTable

The CatalogTable object to check.

column_name_pattern None

(str): Regex pattern to match the model name.

exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

Example(s):

catalog_checks:\n    # DATE columns must end with \"_date\"\n    - name: check_column_name_complies_to_column_type\n      column_name_pattern: .*_date$\n      types:\n        - DATE\n
catalog_checks:\n    # BOOLEAN columns must start with \"is_\"\n    - name: check_column_name_complies_to_column_type\n      column_name_pattern: ^is_.*\n      types:\n        - BOOLEAN\n
catalog_checks:\n    # Columns of all types must consist of lowercase letters and underscores. Note that the specified types depend on the underlying database.\n    - name: check_column_name_complies_to_column_type\n      column_name_pattern: ^[a-z_]*$\n      types:\n        - BIGINT\n        - BOOLEAN\n        - DATE\n        - DOUBLE\n        - INTEGER\n        - VARCHAR\n

Source code in src/dbt_bouncer/checks/catalog/check_columns.py
@pytest.mark.iterate_over_catalog_nodes\n@bouncer_check\ndef check_column_name_complies_to_column_type(\n    request: TopRequest,\n    catalog_node: Union[CatalogTable, None] = None,\n    column_name_pattern: Union[None, str] = None,\n    types: Union[List[str], None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Columns with specified data types must comply to the specified regexp naming pattern.\n\n    Receives:\n        catalog_node (CatalogTable): The CatalogTable object to check.\n        column_name_pattern: (str): Regex pattern to match the model name.\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n\n    Example(s):\n        ```yaml\n        catalog_checks:\n            # DATE columns must end with \"_date\"\n            - name: check_column_name_complies_to_column_type\n              column_name_pattern: .*_date$\n              types:\n                - DATE\n        ```\n        ```yaml\n        catalog_checks:\n            # BOOLEAN columns must start with \"is_\"\n            - name: check_column_name_complies_to_column_type\n              column_name_pattern: ^is_.*\n              types:\n                - BOOLEAN\n        ```\n        ```yaml\n        catalog_checks:\n            # Columns of all types must consist of lowercase letters and underscores. Note that the specified types depend on the underlying database.\n            - name: check_column_name_complies_to_column_type\n              column_name_pattern: ^[a-z_]*$\n              types:\n                - BIGINT\n                - BOOLEAN\n                - DATE\n                - DOUBLE\n                - INTEGER\n                - VARCHAR\n        ```\n    \"\"\"\n\n    non_complying_columns = [\n        v.name\n        for _, v in catalog_node.columns.items()\n        if v.type in types and re.compile(column_name_pattern.strip()).match(v.name) is None  # type: ignore[operator]\n    ]\n\n    assert (\n        not non_complying_columns\n    ), f\"`{catalog_node.unique_id.split('.')[-1]}` has columns that don't comply with the specified regexp pattern (`{column_name_pattern}`): {non_complying_columns}\"\n
"},{"location":"checks/checks_catalog/#catalog.check_columns.check_columns_are_all_documented","title":"check_columns_are_all_documented","text":"

All columns in a model should be included in the model's properties file, i.e. .yml file.

Receives:

Name Type Description catalog_node CatalogTable

The CatalogTable object to check.

exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

Example(s):

catalog_checks:\n    - name: check_columns_are_all_documented\n

Source code in src/dbt_bouncer/checks/catalog/check_columns.py
@pytest.mark.iterate_over_catalog_nodes\n@bouncer_check\ndef check_columns_are_all_documented(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    catalog_node: Union[CatalogTable, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    All columns in a model should be included in the model's properties file, i.e. `.yml` file.\n\n    Receives:\n        catalog_node (CatalogTable): The CatalogTable object to check.\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n\n    Example(s):\n        ```yaml\n        catalog_checks:\n            - name: check_columns_are_all_documented\n        ```\n    \"\"\"\n\n    if catalog_node.unique_id.split(\".\")[0] == \"model\":\n        model = [m for m in models if m.unique_id == catalog_node.unique_id][0]\n        undocumented_columns = [\n            v.name for _, v in catalog_node.columns.items() if v.name not in model.columns.keys()\n        ]\n        assert (\n            not undocumented_columns\n        ), f\"`{catalog_node.unique_id.split('.')[-1]}` has columns that are not included in the models properties file: {undocumented_columns}\"\n
"},{"location":"checks/checks_catalog/#catalog.check_columns.check_columns_are_documented_in_public_models","title":"check_columns_are_documented_in_public_models","text":"

Columns should have a populated description in public models.

Receives:

Name Type Description catalog_node CatalogTable

The CatalogTable object to check.

exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

Example(s):

catalog_checks:\n    - name: check_columns_are_documented_in_public_models\n

Source code in src/dbt_bouncer/checks/catalog/check_columns.py
@pytest.mark.iterate_over_catalog_nodes\n@bouncer_check\ndef check_columns_are_documented_in_public_models(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    catalog_node: Union[CatalogTable, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Columns should have a populated description in public models.\n\n    Receives:\n        catalog_node (CatalogTable): The CatalogTable object to check.\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n\n    Example(s):\n        ```yaml\n        catalog_checks:\n            - name: check_columns_are_documented_in_public_models\n        ```\n    \"\"\"\n\n    if catalog_node.unique_id.split(\".\")[0] == \"model\":\n        model = [m for m in models if m.unique_id == catalog_node.unique_id][0]\n        non_complying_columns = []\n        for k, v in catalog_node.columns.items():\n            if model.access.value == \"public\":\n                column_config = model.columns.get(v.name)\n                if column_config is None or len(column_config.description.strip()) < 4:\n                    non_complying_columns.append(v.name)\n\n        assert (\n            not non_complying_columns\n        ), f\"`{catalog_node.unique_id.split('.')[-1]}` is a public model but has columns that don't have a populated description: {non_complying_columns}\"\n
"},{"location":"checks/checks_catalog/#catalog.check_columns.check_column_has_specified_test","title":"check_column_has_specified_test","text":"

Columns that match the specified regexp pattern must have a specified test.

Receives:

Name Type Description catalog_node CatalogTable

The CatalogTable object to check.

column_name_pattern str

Regex pattern to match the column name.

exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

test_name str

Name of the test to check for.

Example(s):

catalog_checks:\n    - name: check_column_has_specified_test\n      column_name_pattern: ^is_.*\n      test_name: not_null\n

Source code in src/dbt_bouncer/checks/catalog/check_columns.py
@pytest.mark.iterate_over_catalog_nodes\n@bouncer_check\ndef check_column_has_specified_test(\n    request: TopRequest,\n    tests: List[DbtBouncerTest],\n    catalog_node: Union[CatalogTable, None] = None,\n    column_name_pattern: Union[None, str] = None,\n    test_name: Union[None, str] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Columns that match the specified regexp pattern must have a specified test.\n\n    Receives:\n        catalog_node (CatalogTable): The CatalogTable object to check.\n        column_name_pattern (str): Regex pattern to match the column name.\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        test_name (str): Name of the test to check for.\n\n    Example(s):\n        ```yaml\n        catalog_checks:\n            - name: check_column_has_specified_test\n              column_name_pattern: ^is_.*\n              test_name: not_null\n        ```\n    \"\"\"\n\n    columns_to_check = [\n        v.name\n        for _, v in catalog_node.columns.items()\n        if re.compile(column_name_pattern.strip()).match(v.name) is not None\n    ]\n    relevant_tests = [\n        t\n        for t in tests\n        if t.test_metadata.name == test_name and t.attached_node == catalog_node.unique_id\n    ]\n    non_complying_columns = [\n        c\n        for c in columns_to_check\n        if f\"{catalog_node.unique_id}.{c}\"\n        not in [f\"{t.attached_node}.{t.column_name}\" for t in relevant_tests]\n    ]\n\n    assert (\n        not non_complying_columns\n    ), f\"`{catalog_node.unique_id.split('.')[-1]}` has columns that should have a `{test_name}` test: {non_complying_columns}\"\n
"},{"location":"checks/checks_manifest/","title":"Manifest Checks","text":"

Note

The below checks require manifest.json to be present.

"},{"location":"checks/checks_manifest/#manifest.check_exposures","title":"check_exposures","text":""},{"location":"checks/checks_manifest/#manifest.check_exposures.check_exposure_based_on_non_public_models","title":"check_exposure_based_on_non_public_models","text":"

Exposures should be based on public models only.

Receives:

Name Type Description exclude Optional[str]

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.

exposure Exposures

The Exposures object to check.

include Optional[str]

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.

Example(s):

manifest_checks:\n    - name: check_exposure_based_on_non_public_models\n

Source code in src/dbt_bouncer/checks/manifest/check_exposures.py
@pytest.mark.iterate_over_exposures\n@bouncer_check\ndef check_exposure_based_on_non_public_models(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    exposure: Union[Exposures, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Exposures should be based on public models only.\n\n    Receives:\n        exclude (Optional[str]): 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.\n        exposure (Exposures): The Exposures object to check.\n        include (Optional[str]): 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.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_exposure_based_on_non_public_models\n        ```\n    \"\"\"\n\n    non_public_upstream_dependencies = []\n    for model in exposure.depends_on.nodes:\n        if model.split(\".\")[0] == \"model\" and model.split(\".\")[1] == exposure.package_name:\n            model = [m for m in models if m.unique_id == model][0]\n            if model.access.value != \"public\":\n                non_public_upstream_dependencies.append(model.name)\n\n    assert (\n        not non_public_upstream_dependencies\n    ), f\"`{exposure.name}` is based on a model(s) that is not public: {non_public_upstream_dependencies}.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_exposures.check_exposure_based_on_view","title":"check_exposure_based_on_view","text":"

Exposures should not be based on views.

Receives:

Name Type Description exclude Optional[str]

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.

exposure Exposures

The Exposures object to check.

include Optional[str]

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.

materializations_to_include Optional[List[str]]

List of materializations to include in the check. If not provided, defaults to ephemeral and view.

Example(s):

manifest_checks:\n    - name: check_exposure_based_on_view\n
manifest_checks:\n    - name: check_exposure_based_on_view\n      materializations_to_include:\n        - ephemeral\n        - my_custom_materialization\n        - view\n

Source code in src/dbt_bouncer/checks/manifest/check_exposures.py
@pytest.mark.iterate_over_exposures\n@bouncer_check\ndef check_exposure_based_on_view(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    exposure: Union[Exposures, None] = None,\n    materializations_to_include: Union[List[str], None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Exposures should not be based on views.\n\n    Receives:\n        exclude (Optional[str]): 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.\n        exposure (Exposures): The Exposures object to check.\n        include (Optional[str]): 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.\n        materializations_to_include (Optional[List[str]]): List of materializations to include in the check. If not provided, defaults to `ephemeral` and `view`.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_exposure_based_on_view\n        ```\n        ```yaml\n        manifest_checks:\n            - name: check_exposure_based_on_view\n              materializations_to_include:\n                - ephemeral\n                - my_custom_materialization\n                - view\n        ```\n    \"\"\"\n\n    non_table_upstream_dependencies = []\n    for model in exposure.depends_on.nodes:\n        if model.split(\".\")[0] == \"model\" and model.split(\".\")[1] == exposure.package_name:\n            model = [m for m in models if m.unique_id == model][0]\n            if model.config.materialized in materializations_to_include:  # type: ignore[operator]\n                non_table_upstream_dependencies.append(model.name)\n\n    assert (\n        not non_table_upstream_dependencies\n    ), f\"`{exposure.name}` is based on a model that is not a table: {non_table_upstream_dependencies}.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_lineage","title":"check_lineage","text":""},{"location":"checks/checks_manifest/#manifest.check_lineage.check_lineage_permitted_upstream_models","title":"check_lineage_permitted_upstream_models","text":"

Upstream models must have a path that matches the provided upstream_path_pattern.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

upstream_path_pattern str

Regexp pattern to match the upstream model(s) path.

Example(s):

manifest_checks:\n    - name: check_lineage_permitted_upstream_models\n      include: ^models/staging\n      upstream_path_pattern: $^\n    - name: check_lineage_permitted_upstream_models\n      include: ^models/intermediate\n      upstream_path_pattern: ^models/staging|^models/intermediate\n    - name: check_lineage_permitted_upstream_models\n      include: ^models/marts\n      upstream_path_pattern: ^models/staging|^models/intermediate\n

Source code in src/dbt_bouncer/checks/manifest/check_lineage.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_lineage_permitted_upstream_models(\n    manifest_obj: DbtBouncerManifest,\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    model: Union[DbtBouncerModel, None] = None,\n    upstream_path_pattern: Union[None, str] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Upstream models must have a path that matches the provided `upstream_path_pattern`.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n        upstream_path_pattern (str): Regexp pattern to match the upstream model(s) path.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_lineage_permitted_upstream_models\n              include: ^models/staging\n              upstream_path_pattern: $^\n            - name: check_lineage_permitted_upstream_models\n              include: ^models/intermediate\n              upstream_path_pattern: ^models/staging|^models/intermediate\n            - name: check_lineage_permitted_upstream_models\n              include: ^models/marts\n              upstream_path_pattern: ^models/staging|^models/intermediate\n        ```\n    \"\"\"\n\n    upstream_models = [\n        x\n        for x in model.depends_on.nodes\n        if x.split(\".\")[0] == \"model\"\n        and x.split(\".\")[1] == manifest_obj.manifest.metadata.project_name\n    ]\n    not_permitted_upstream_models = [\n        upstream_model\n        for upstream_model in upstream_models\n        if re.compile(upstream_path_pattern.strip()).match(\n            [m for m in models if m.unique_id == upstream_model][0].original_file_path\n        )\n        is None\n    ]\n    assert (\n        not not_permitted_upstream_models\n    ), f\"`{model.name}` references upstream models that are not permitted: {[m.split('.')[-1] for m in not_permitted_upstream_models]}.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_lineage.check_lineage_seed_cannot_be_used","title":"check_lineage_seed_cannot_be_used","text":"

Seed cannot be referenced in models with a path that matches the specified include config.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_lineage_seed_cannot_be_used\n      include: ^models/intermediate|^models/marts\n

Source code in src/dbt_bouncer/checks/manifest/check_lineage.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_lineage_seed_cannot_be_used(\n    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Seed cannot be referenced in models with a path that matches the specified `include` config.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_lineage_seed_cannot_be_used\n              include: ^models/intermediate|^models/marts\n        ```\n    \"\"\"\n\n    assert not [\n        x for x in model.depends_on.nodes if x.split(\".\")[0] == \"seed\"\n    ], f\"`{model.name}` references a seed even though this is not permitted.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_lineage.check_lineage_source_cannot_be_used","title":"check_lineage_source_cannot_be_used","text":"

Sources cannot be referenced in models with a path that matches the specified include config.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_lineage_source_cannot_be_used\n      include: ^models/intermediate|^models/marts\n

Source code in src/dbt_bouncer/checks/manifest/check_lineage.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_lineage_source_cannot_be_used(\n    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Sources cannot be referenced in models with a path that matches the specified `include` config.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_lineage_source_cannot_be_used\n              include: ^models/intermediate|^models/marts\n        ```\n    \"\"\"\n\n    assert not [\n        x for x in model.depends_on.nodes if x.split(\".\")[0] == \"source\"\n    ], f\"`{model.name}` references a source even though this is not permitted.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_macros","title":"check_macros","text":""},{"location":"checks/checks_manifest/#manifest.check_macros.check_macro_arguments_description_populated","title":"check_macro_arguments_description_populated","text":"

Macro arguments must have a populated description.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.

macro Macros

The Macros object to check.

Example(s):

manifest_checks:\n    - name: check_macro_arguments_description_populated\n
# Only \"common\" macros need to have their arguments populated\nmanifest_checks:\n    - name: check_macro_arguments_description_populated\n      include: ^macros/common\n

Source code in src/dbt_bouncer/checks/manifest/check_macros.py
@pytest.mark.iterate_over_macros\n@bouncer_check\ndef check_macro_arguments_description_populated(\n    request: TopRequest, macro: Union[Macros, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Macro arguments must have a populated description.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.\n        macro (Macros): The Macros object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_macro_arguments_description_populated\n        ```\n        ```yaml\n        # Only \"common\" macros need to have their arguments populated\n        manifest_checks:\n            - name: check_macro_arguments_description_populated\n              include: ^macros/common\n        ```\n    \"\"\"\n\n    environment = jinja2.Environment(extensions=[TagExtension])\n    ast = environment.parse(macro.macro_sql)\n\n    # Assume macro is a \"true\" macro, if not see if it's a generic test\n    try:\n        macro_arguments = [a.name for a in ast.body[0].args]  # type: ignore[attr-defined]\n    except AttributeError:\n        test_macro = [x for x in ast.body if not isinstance(x.nodes[0], jinja2.nodes.Call)][0]  # type: ignore[attr-defined]\n        macro_arguments = [x.name for x in test_macro.nodes if isinstance(x, jinja2.nodes.Name)]  # type: ignore[attr-defined]\n\n    # macro_arguments: List of args parsed from macro SQL\n    # macro.arguments: List of args manually added to the properties file\n\n    non_complying_args = []\n    for arg in macro_arguments:\n        macro_doc_raw = [x for x in macro.arguments if x.name == arg]\n        if macro_doc_raw == []:\n            non_complying_args.append(arg)\n        elif (\n            arg not in [x.name for x in macro.arguments]\n            or len(macro_doc_raw[0].description.strip()) <= 4\n        ):\n            non_complying_args.append(arg)\n\n    assert (\n        non_complying_args == []\n    ), f\"Macro `{macro.name}` does not have a populated description for the following argument(s): {non_complying_args}.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_macros.check_macro_code_does_not_contain_regexp_pattern","title":"check_macro_code_does_not_contain_regexp_pattern","text":"

The raw code for a macro must not match the specified regexp pattern.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.

macro Macros

The Macros object to check.

regexp_pattern str

The regexp pattern that should not be matched by the macro code.

Example(s):

manifest_checks:\n    # Prefer `coalesce` over `ifnull`: https://docs.sqlfluff.com/en/stable/rules.html#sqlfluff.rules.sphinx.Rule_CV02\n    - name: check_macro_code_does_not_contain_regexp_pattern\n      regexp_pattern: .*[i][f][n][u][l][l].*\n

Source code in src/dbt_bouncer/checks/manifest/check_macros.py
@pytest.mark.iterate_over_macros\n@bouncer_check\ndef check_macro_code_does_not_contain_regexp_pattern(\n    request: TopRequest,\n    macro: Union[Macros, None] = None,\n    regexp_pattern: Union[None, str] = None,\n    **kwargs,\n) -> None:\n    \"\"\"The raw code for a macro must not match the specified regexp pattern.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.\n        macro (Macros): The Macros object to check.\n        regexp_pattern (str): The regexp pattern that should not be matched by the macro code.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            # Prefer `coalesce` over `ifnull`: https://docs.sqlfluff.com/en/stable/rules.html#sqlfluff.rules.sphinx.Rule_CV02\n            - name: check_macro_code_does_not_contain_regexp_pattern\n              regexp_pattern: .*[i][f][n][u][l][l].*\n        ```\n    \"\"\"\n\n    assert (\n        re.compile(regexp_pattern.strip(), flags=re.DOTALL).match(macro.macro_sql) is None\n    ), f\"Macro `{macro.name}` contains a banned string: `{regexp_pattern.strip()}`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_macros.check_macro_description_populated","title":"check_macro_description_populated","text":"

Macros must have a populated description.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.

macro Macros

The Macros object to check.

Example(s):

manifest_checks:\n    - name: check_macro_description_populated\n
# Only \"common\" macros need to have a populated description\nmanifest_checks:\n    - name: check_macro_description_populated\n      include: ^macros/common\n

Source code in src/dbt_bouncer/checks/manifest/check_macros.py
@pytest.mark.iterate_over_macros\n@bouncer_check\ndef check_macro_description_populated(\n    request: TopRequest, macro: Union[Macros, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Macros must have a populated description.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.\n        macro (Macros): The Macros object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_macro_description_populated\n        ```\n        ```yaml\n        # Only \"common\" macros need to have a populated description\n        manifest_checks:\n            - name: check_macro_description_populated\n              include: ^macros/common\n        ```\n    \"\"\"\n\n    assert (\n        len(macro.description.strip()) > 4\n    ), f\"Macro `{macro.name}` does not have a populated description.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_macros.check_macro_max_number_of_lines","title":"check_macro_max_number_of_lines","text":"

Macros may not have more than the specified number of lines.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.

macro Macros

The Macros object to check.

max_number_of_lines int

The maximum number of permitted lines. Default: 50.

Example(s):

manifest_checks:\n    - name: check_macro_max_number_of_lines\n
manifest_checks:\n    - name: check_macro_max_number_of_lines\n      max_number_of_lines: 100\n

Source code in src/dbt_bouncer/checks/manifest/check_macros.py
@pytest.mark.iterate_over_macros\n@bouncer_check\ndef check_macro_max_number_of_lines(\n    request: TopRequest,\n    macro: Union[Macros, None] = None,\n    max_number_of_lines: Union[int, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Macros may not have more than the specified number of lines.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.\n        macro (Macros): The Macros object to check.\n        max_number_of_lines (int): The maximum number of permitted lines. Default: 50.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_macro_max_number_of_lines\n        ```\n        ```yaml\n        manifest_checks:\n            - name: check_macro_max_number_of_lines\n              max_number_of_lines: 100\n        ```\n    \"\"\"\n\n    actual_number_of_lines = macro.macro_sql.count(\"\\n\") + 1\n\n    assert (\n        actual_number_of_lines <= max_number_of_lines\n    ), f\"Macro `{macro.name}` has {actual_number_of_lines} lines, this is more than the maximum permitted number of lines ({max_number_of_lines}).\"\n
"},{"location":"checks/checks_manifest/#manifest.check_macros.check_macro_name_matches_file_name","title":"check_macro_name_matches_file_name","text":"

Macros names must be the same as the file they are contained in.

Generic tests are also macros, however to document these tests the \"name\" value must be precededed with \"test_\".

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.

macro Macros

The Macros object to check.

Example(s):

manifest_checks:\n    - name: check_macro_name_matches_file_name\n

Source code in src/dbt_bouncer/checks/manifest/check_macros.py
@pytest.mark.iterate_over_macros\n@bouncer_check\ndef check_macro_name_matches_file_name(\n    request: TopRequest, macro: Union[Macros, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Macros names must be the same as the file they are contained in.\n\n    Generic tests are also macros, however to document these tests the \"name\" value must be precededed with \"test_\".\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.\n        macro (Macros): The Macros object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_macro_name_matches_file_name\n        ```\n    \"\"\"\n\n    if macro.name.startswith(\"test_\"):\n        assert (\n            macro.name[5:] == macro.original_file_path.split(\"/\")[-1].split(\".\")[0]\n        ), f\"Macro `{macro.unique_id}` is not in a file named `{macro.name[5:]}.sql`.\"\n    else:\n        assert (\n            macro.name == macro.original_file_path.split(\"/\")[-1].split(\".\")[0]\n        ), f\"Macro `{macro.name}` is not in a file of the same name.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_macros.check_macro_property_file_location","title":"check_macro_property_file_location","text":"

Macro properties files must follow the guidance provided by dbt here.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.

macro Macros

The Macros object to check.

Example(s):

manifest_checks:\n    - name: check_macro_property_file_location\n

Source code in src/dbt_bouncer/checks/manifest/check_macros.py
@pytest.mark.iterate_over_macros\n@bouncer_check\ndef check_macro_property_file_location(\n    request: TopRequest, macro: Union[Macros, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Macro properties files must follow the guidance provided by dbt [here](https://docs.getdbt.com/best-practices/how-we-structure/5-the-rest-of-the-project#how-we-use-the-other-folders).\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the macro path. Macro paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the macro path. Only macro paths that match the pattern will be checked.\n        macro (Macros): The Macros object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_macro_property_file_location\n        ```\n    \"\"\"\n\n    expected_substr = \"_\".join(macro.original_file_path[6:].split(\"/\")[:-1])\n    properties_yml_name = macro.patch_path.split(\"/\")[-1]\n\n    if macro.original_file_path.startswith(\n        \"tests/\"\n    ):  # Do not check generic tests (which are also macros)\n        pass\n    elif expected_substr == \"\":  # i.e. macro in ./macros\n        assert (\n            properties_yml_name == \"_macros.yml\"\n        ), f\"The properties file for `{macro.name}` (`{properties_yml_name}`) should be `_macros.yml`.\"\n    else:\n        assert properties_yml_name.startswith(\n            \"_\"\n        ), f\"The properties file for `{macro.name}` (`{properties_yml_name}`) does not start with an underscore.\"\n        assert (\n            expected_substr in properties_yml_name\n        ), f\"The properties file for `{macro.name}` (`{properties_yml_name}`) does not contain the expected substring (`{expected_substr}`).\"\n        assert properties_yml_name.endswith(\n            \"__macros.yml\"\n        ), f\"The properties file for `{macro.name.name}` (`{properties_yml_name}`) does not end with `__macros.yml`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_metadata","title":"check_metadata","text":""},{"location":"checks/checks_manifest/#manifest.check_metadata.check_project_name","title":"check_project_name","text":"

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>.

Receives:

Type Description None

project_name_pattern str: Regex pattern to match the project name.

Example(s):

manifest_checks:\n    - name: check_project_name\n      project_name_pattern: ^awesome_company_\n

Source code in src/dbt_bouncer/checks/manifest/check_metadata.py
@bouncer_check\ndef check_project_name(\n    manifest_obj: DbtBouncerManifest,\n    request: TopRequest,\n    project_name_pattern: Union[None, str] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    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>`.\n\n    Receives:\n        project_name_pattern str: Regex pattern to match the project name.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_project_name\n              project_name_pattern: ^awesome_company_\n        ```\n    \"\"\"\n\n    assert (\n        re.compile(project_name_pattern.strip()).match(manifest_obj.manifest.metadata.project_name)\n        is not None\n    ), f\"Project name (`{manifest_obj.manifest.metadata.project_name}`) does not conform to the supplied regex `({project_name_pattern.strip()})`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models","title":"check_models","text":""},{"location":"checks/checks_manifest/#manifest.check_models.check_model_access","title":"check_model_access","text":"

Models must have the specified access attribute. Requires dbt 1.7+.

Receives:

Name Type Description access Literal['private', 'protected', 'public']

The access level to check for.

exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    # Align with dbt best practices that marts should be `public`, everything else should be `protected`\n    - name: check_model_access\n      access: protected\n      include: ^models/intermediate\n    - name: check_model_access\n      access: public\n      include: ^models/marts\n    - name: check_model_access\n      access: protected\n      include: ^models/staging\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_access(\n    request: TopRequest,\n    access: Union[None, str] = None,\n    model: Union[DbtBouncerModel, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Models must have the specified access attribute. Requires dbt 1.7+.\n\n    Receives:\n        access (Literal[\"private\", \"protected\", \"public\"]): The access level to check for.\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            # Align with dbt best practices that marts should be `public`, everything else should be `protected`\n            - name: check_model_access\n              access: protected\n              include: ^models/intermediate\n            - name: check_model_access\n              access: public\n              include: ^models/marts\n            - name: check_model_access\n              access: protected\n              include: ^models/staging\n        ```\n    \"\"\"\n\n    assert (\n        model.access.value == access\n    ), f\"`{model.name}` has `{model.access.value}` access, it should have access `{access}`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_contract_enforced_for_public_model","title":"check_model_contract_enforced_for_public_model","text":"

Public models must have contracts enforced.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_contract_enforced_for_public_model\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_contract_enforced_for_public_model(\n    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Public models must have contracts enforced.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_contract_enforced_for_public_model\n        ```\n    \"\"\"\n\n    if model.access.value == \"public\":\n        assert (\n            model.contract.enforced is True\n        ), f\"`{model.name}` is a public model but does not have contracts enforced.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_description_populated","title":"check_model_description_populated","text":"

Models must have a populated description.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_description_populated\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_description_populated(\n    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Models must have a populated description.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_description_populated\n        ```\n    \"\"\"\n\n    assert (\n        len(model.description.strip()) > 4\n    ), f\"`{model.name}` does not have a populated description.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_documentation_coverage","title":"check_model_documentation_coverage","text":"

Set the minimum percentage of models that have a populated description.

Receives:

Name Type Description min_model_documentation_coverage_pct float

The minimum percentage of models that must have a populated description. Default: 100.

Example(s):

manifest_checks:\n    - name: check_model_description_populated\n      min_model_documentation_coverage_pct: 90\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@bouncer_check\ndef check_model_documentation_coverage(\n    request: TopRequest,\n    models: List[DbtBouncerModel],\n    min_model_documentation_coverage_pct: Union[float, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Set the minimum percentage of models that have a populated description.\n\n    Receives:\n        min_model_documentation_coverage_pct (float): The minimum percentage of models that must have a populated description. Default: 100.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_description_populated\n              min_model_documentation_coverage_pct: 90\n        ```\n    \"\"\"\n\n    num_models = len(models)\n    models_with_description = []\n    for model in models:\n        if len(model.description.strip()) > 4:\n            models_with_description.append(model.unique_id)\n\n    num_models_with_descriptions = len(models_with_description)\n    model_description_coverage_pct = (num_models_with_descriptions / num_models) * 100\n\n    assert (\n        model_description_coverage_pct >= min_model_documentation_coverage_pct  # type: ignore[operator]\n    ), f\"Only {model_description_coverage_pct}% of models have a populated description, this is less than the permitted minimum of {min_model_documentation_coverage_pct}%.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_documented_in_same_directory","title":"check_model_documented_in_same_directory","text":"

Models must be documented in the same directory where they are defined (i.e. .yml and .sql files are in the same directory).

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_documented_in_same_directory\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_documented_in_same_directory(\n    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Models must be documented in the same directory where they are defined (i.e. `.yml` and `.sql` files are in the same directory).\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_documented_in_same_directory\n        ```\n    \"\"\"\n\n    model_doc_dir = model.patch_path[model.patch_path.find(\"models\") :].split(\"/\")[:-1]\n    model_sql_dir = model.original_file_path.split(\"/\")[:-1]\n\n    assert (\n        model_doc_dir == model_sql_dir\n    ), f\"`{model.name}` is documented in a different directory to the `.sql` file: `{'/'.join(model_doc_dir)}` vs `{'/'.join(model_sql_dir)}`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_code_does_not_contain_regexp_pattern","title":"check_model_code_does_not_contain_regexp_pattern","text":"

The raw code for a model must not match the specified regexp pattern.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

regexp_pattern str

The regexp pattern that should not be matched by the model code.

Example(s):

manifest_checks:\n    # Prefer `coalesce` over `ifnull`: https://docs.sqlfluff.com/en/stable/rules.html#sqlfluff.rules.sphinx.Rule_CV02\n    - name: check_model_code_does_not_contain_regexp_pattern\n      regexp_pattern: .*[i][f][n][u][l][l].*\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_code_does_not_contain_regexp_pattern(\n    request: TopRequest,\n    model: Union[DbtBouncerModel, None] = None,\n    regexp_pattern: Union[None, str] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    The raw code for a model must not match the specified regexp pattern.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n        regexp_pattern (str): The regexp pattern that should not be matched by the model code.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            # Prefer `coalesce` over `ifnull`: https://docs.sqlfluff.com/en/stable/rules.html#sqlfluff.rules.sphinx.Rule_CV02\n            - name: check_model_code_does_not_contain_regexp_pattern\n              regexp_pattern: .*[i][f][n][u][l][l].*\n        ```\n    \"\"\"\n\n    assert (\n        re.compile(regexp_pattern.strip(), flags=re.DOTALL).match(model.raw_code) is None\n    ), f\"`{model.name}` contains a banned string: `{regexp_pattern.strip()}`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_depends_on_multiple_sources","title":"check_model_depends_on_multiple_sources","text":"

Models cannot reference more than one source.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_depends_on_multiple_sources\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_depends_on_multiple_sources(\n    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Models cannot reference more than one source.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_depends_on_multiple_sources\n        ```\n    \"\"\"\n\n    num_reffed_sources = sum(x.split(\".\")[0] == \"source\" for x in model.depends_on.nodes)\n    assert num_reffed_sources <= 1, f\"`{model.name}` references more than one source.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_directories","title":"check_model_directories","text":"

Only specified sub-directories are permitted.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked. Special case: if you want to check ./models, pass \"\" to include.

model DbtBouncerModel

The DbtBouncerModel object to check.

permitted_sub_directories List[str]

List of permitted sub-directories.

Example(s):

manifest_checks:\n- name: check_model_directories\n  include: models\n  permitted_sub_directories:\n    - intermediate\n    - marts\n    - staging\n
# Restrict sub-directories within `./models/staging`\n- name: check_model_directories\n  include: ^models/staging\n  permitted_sub_directories:\n    - crm\n    - payments\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_directories(\n    request: TopRequest,\n    include: Union[None, str] = None,\n    model: Union[DbtBouncerModel, None] = None,\n    permitted_sub_directories: Union[List[str], None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Only specified sub-directories are permitted.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked. Special case: if you want to check `./models`, pass \"\" to `include`.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n        permitted_sub_directories (List[str]): List of permitted sub-directories.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n        - name: check_model_directories\n          include: models\n          permitted_sub_directories:\n            - intermediate\n            - marts\n            - staging\n        ```\n        ```yaml\n        # Restrict sub-directories within `./models/staging`\n        - name: check_model_directories\n          include: ^models/staging\n          permitted_sub_directories:\n            - crm\n            - payments\n        ```\n    \"\"\"\n\n    matched_path = re.compile(include.strip()).match(model.original_file_path)\n    path_after_match = model.original_file_path[matched_path.end() + 1 :]  # type: ignore[union-attr]\n\n    assert (\n        path_after_match.split(\"/\")[0] in permitted_sub_directories  # type: ignore[operator]\n    ), f\"`{model.name}` is located in `{model.original_file_path.split('/')[1]}`, this is not a valid sub-directory. {path_after_match.split('/')[0]}\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_has_contracts_enforced","title":"check_model_has_contracts_enforced","text":"

Model must have contracts enforced.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_has_contracts_enforced\n      include: ^models/marts\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_has_contracts_enforced(\n    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Model must have contracts enforced.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_has_contracts_enforced\n              include: ^models/marts\n        ```\n    \"\"\"\n\n    assert model.contract.enforced is True, f\"`{model.name}` does not have contracts enforced.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_has_meta_keys","title":"check_model_has_meta_keys","text":"

The meta config for models must have the specified keys.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

keys NestedDict

A list (that may contain sub-lists) of required keys.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_has_meta_keys\n      keys:\n        - maturity\n        - owner\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_has_meta_keys(\n    request: TopRequest,\n    keys: Union[NestedDict, None] = None,\n    model: Union[DbtBouncerModel, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    The `meta` config for models must have the specified keys.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        keys (NestedDict): A list (that may contain sub-lists) of required keys.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_has_meta_keys\n              keys:\n                - maturity\n                - owner\n        ```\n    \"\"\"\n\n    missing_keys = find_missing_meta_keys(\n        meta_config=model.meta,\n        required_keys=keys.model_dump(),\n    )\n    assert (\n        missing_keys == []\n    ), f\"`{model.name}` is missing the following keys from the `meta` config: {[x.replace('>>', '') for x in missing_keys]}\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_has_no_upstream_dependencies","title":"check_model_has_no_upstream_dependencies","text":"

Identify if models have no upstream dependencies as this likely indicates hard-coded tables references.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_has_no_upstream_dependencies\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_has_no_upstream_dependencies(\n    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Identify if models have no upstream dependencies as this likely indicates hard-coded tables references.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_has_no_upstream_dependencies\n        ```\n    \"\"\"\n\n    assert (\n        len(model.depends_on.nodes) > 0\n    ), f\"`{model.name}` has no upstream dependencies, this likely indicates hard-coded tables references.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_has_tags","title":"check_model_has_tags","text":"

Models must have the specified tags.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

tags List[str]

List of tags to check for.

Example(s):

manifest_checks:\n    - name: check_model_has_tags\n      tags:\n        - tag_1\n        - tag_2\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_has_tags(\n    request: TopRequest,\n    model: Union[DbtBouncerModel, None] = None,\n    tags: Union[List[str], None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Models must have the specified tags.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n        tags (List[str]): List of tags to check for.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_has_tags\n              tags:\n                - tag_1\n                - tag_2\n        ```\n    \"\"\"\n\n    missing_tags = [tag for tag in tags if tag not in model.tags]\n    assert not missing_tags, f\"`{model.name}` is missing required tags: {missing_tags}.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_has_unique_test","title":"check_model_has_unique_test","text":"

Models must have a test for uniqueness of a column.

Receives:

Name Type Description accepted_uniqueness_tests Optional[List[str]]

List of tests that are accepted as uniqueness tests. If not provided, defaults to expect_compound_columns_to_be_unique, dbt_utils.unique_combination_of_columns and unique.

exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n  - name: check_model_has_unique_test\n    include: ^models/marts\n
manifest_checks:\n  # Example of allowing a custom uniqueness test\n  - name: check_model_has_unique_test\n    accepted_uniqueness_tests:\n        - expect_compound_columns_to_be_unique\n        - my_custom_uniqueness_test\n        - unique\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_has_unique_test(\n    request: TopRequest,\n    tests: List[DbtBouncerModel],\n    accepted_uniqueness_tests: Union[List[str], None] = None,\n    model: Union[DbtBouncerModel, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Models must have a test for uniqueness of a column.\n\n    Receives:\n        accepted_uniqueness_tests (Optional[List[str]]): List of tests that are accepted as uniqueness tests. If not provided, defaults to `expect_compound_columns_to_be_unique`, `dbt_utils.unique_combination_of_columns` and `unique`.\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n          - name: check_model_has_unique_test\n            include: ^models/marts\n        ```\n        ```yaml\n        manifest_checks:\n          # Example of allowing a custom uniqueness test\n          - name: check_model_has_unique_test\n            accepted_uniqueness_tests:\n                - expect_compound_columns_to_be_unique\n                - my_custom_uniqueness_test\n                - unique\n        ```\n    \"\"\"\n\n    num_unique_tests = sum(\n        test.attached_node == model.unique_id\n        and test.test_metadata.name in accepted_uniqueness_tests  # type: ignore[operator]\n        for test in tests\n    )\n    assert (\n        num_unique_tests >= 1\n    ), f\"`{model.name}` does not have a test for uniqueness of a column.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_has_unit_tests","title":"check_model_has_unit_tests","text":"

Models must have more than the specified number of unit tests.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

min_number_of_unit_tests Optional[int]

The minimum number of unit tests that a model must have. Default: 1.

model DbtBouncerModel

The DbtBouncerModel object to check.

Warning

This check is only supported for dbt 1.8.0 and above.

Example(s):

manifest_checks:\n    - name: check_model_has_unit_tests\n      include: ^models/marts\n
manifest_checks:\n    - name: check_model_has_unit_tests\n      min_number_of_unit_tests: 2\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_has_unit_tests(\n    manifest_obj: DbtBouncerManifest,\n    request: TopRequest,\n    unit_tests: List[UnitTests],\n    min_number_of_unit_tests: Union[int, None] = None,\n    model: Union[DbtBouncerModel, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Models must have more than the specified number of unit tests.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        min_number_of_unit_tests (Optional[int]): The minimum number of unit tests that a model must have. Default: 1.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    !!! warning\n\n        This check is only supported for dbt 1.8.0 and above.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_has_unit_tests\n              include: ^models/marts\n        ```\n        ```yaml\n        manifest_checks:\n            - name: check_model_has_unit_tests\n              min_number_of_unit_tests: 2\n        ```\n    \"\"\"\n\n    if semver.Version.parse(manifest_obj.manifest.metadata.dbt_version) >= \"1.8.0\":\n        num_unit_tests = len(\n            [t.unique_id for t in unit_tests if t.depends_on.nodes[0] == model.unique_id]\n        )\n        assert (\n            num_unit_tests >= min_number_of_unit_tests  # type: ignore[operator]\n        ), f\"`{model.name}` has {num_unit_tests} unit tests, this is less than the minimum of {min_number_of_unit_tests}.\"\n    else:\n        logging.warning(\n            \"The `check_model_has_unit_tests` check is only supported for dbt 1.8.0 and above.\"\n        )\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_max_chained_views","title":"check_model_max_chained_views","text":"

Models cannot have more than the specified number of upstream dependents that are not tables (default: 3).

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

materializations_to_include Optional[List[str]]

List of materializations to include in the check. If not provided, defaults to ephemeral and view.

max_chained_views Optional[int]

The maximum number of upstream dependents that are not tables. Default: 3

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_max_chained_views\n
manifest_checks:\n    - name: check_model_max_chained_views\n      materializations_to_include:\n        - ephemeral\n        - my_custom_materialization\n        - view\n      max_chained_views: 5\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_max_chained_views(\n    manifest_obj: DbtBouncerManifest,\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    materializations_to_include: Union[List[str], None] = None,\n    max_chained_views: Union[int, None] = None,\n    model: Union[DbtBouncerModel, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Models cannot have more than the specified number of upstream dependents that are not tables (default: 3).\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        materializations_to_include (Optional[List[str]]): List of materializations to include in the check. If not provided, defaults to `ephemeral` and `view`.\n        max_chained_views (Optional[int]): The maximum number of upstream dependents that are not tables. Default: 3\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_max_chained_views\n        ```\n        ```yaml\n        manifest_checks:\n            - name: check_model_max_chained_views\n              materializations_to_include:\n                - ephemeral\n                - my_custom_materialization\n                - view\n              max_chained_views: 5\n        ```\n    \"\"\"\n\n    def return_upstream_view_models(\n        materializations,\n        max_chained_views,\n        models,\n        model_unique_ids_to_check,\n        package_name,\n        depth=0,\n    ):\n        \"\"\"\n        Recursive function to return model unique_id's of upstream models that are views. Depth of recursion can be specified. If no models meet the criteria then an empty list is returned.\n        \"\"\"\n\n        if depth == max_chained_views or model_unique_ids_to_check == []:\n            return model_unique_ids_to_check\n\n        relevant_upstream_models = []\n        for model in model_unique_ids_to_check:\n            upstream_nodes = list(\n                [m2 for m2 in models if m2.unique_id == model][0].depends_on.nodes\n            )\n            if upstream_nodes != []:\n                upstream_models = [\n                    m\n                    for m in upstream_nodes\n                    if m.split(\".\")[0] == \"model\" and m.split(\".\")[1] == package_name\n                ]\n                for i in upstream_models:\n                    if [m for m in models if m.unique_id == i][\n                        0\n                    ].config.materialized in materializations:\n                        relevant_upstream_models.append(i)\n\n        depth += 1\n        return return_upstream_view_models(\n            materializations=materializations,\n            max_chained_views=max_chained_views,\n            models=models,\n            model_unique_ids_to_check=relevant_upstream_models,\n            package_name=package_name,\n            depth=depth,\n        )\n\n    assert (\n        len(\n            return_upstream_view_models(\n                materializations=materializations_to_include,\n                max_chained_views=max_chained_views,\n                models=models,\n                model_unique_ids_to_check=[model.unique_id],\n                package_name=manifest_obj.manifest.metadata.project_name,\n            )\n        )\n        == 0\n    ), f\"`{model.name}` has more than {max_chained_views} upstream dependents that are not tables.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_max_fanout","title":"check_model_max_fanout","text":"

Models cannot have more than the specified number of downstream models (default: 3).

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

max_downstream_models Optional[int]

The maximum number of permitted downstream models. Default: 3

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_max_fanout\n      max_downstream_models: 2\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_max_fanout(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    max_downstream_models: Union[int, None] = None,\n    model: Union[DbtBouncerModel, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Models cannot have more than the specified number of downstream models (default: 3).\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        max_downstream_models (Optional[int]): The maximum number of permitted downstream models. Default: 3\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_max_fanout\n              max_downstream_models: 2\n        ```\n    \"\"\"\n\n    num_downstream_models = sum(model.unique_id in m.depends_on.nodes for m in models)\n\n    assert (\n        num_downstream_models <= max_downstream_models  # type: ignore[operator]\n    ), f\"`{model.name}` has {num_downstream_models} downstream models, which is more than the permitted maximum of {max_downstream_models}.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_max_number_of_lines","title":"check_model_max_number_of_lines","text":"

Models may not have more than the specified number of lines.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern

None

will be checked.

max_number_of_lines int

The maximum number of permitted lines. Default: 100.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_max_number_of_lines\n
manifest_checks:\n    - name: check_model_max_number_of_lines\n      max_number_of_lines: 150\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_max_number_of_lines(\n    request: TopRequest,\n    max_number_of_lines: Union[int, None] = None,\n    model: Union[DbtBouncerModel, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Models may not have more than the specified number of lines.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern\n        will be checked.\n        max_number_of_lines (int): The maximum number of permitted lines. Default: 100.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_max_number_of_lines\n        ```\n        ```yaml\n        manifest_checks:\n            - name: check_model_max_number_of_lines\n              max_number_of_lines: 150\n        ```\n    \"\"\"\n\n    actual_number_of_lines = model.raw_code.count(\"\\n\") + 1\n\n    assert (\n        actual_number_of_lines <= max_number_of_lines\n    ), f\"`{model.name}` has {actual_number_of_lines} lines, this is more than the maximum permitted number of lines ({max_number_of_lines}).\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_max_upstream_dependencies","title":"check_model_max_upstream_dependencies","text":"

Limit the number of upstream dependencies a model has. Default values are 5 for models, 5 for macros, and 1 for sources.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

max_upstream_macros Optional[int]

The maximum number of permitted upstream macros. Default: 5

max_upstream_models Optional[int]

The maximum number of permitted upstream models. Default: 5

max_upstream_sources Optional[int]

The maximum number of permitted upstream sources. Default: 1

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_max_upstream_dependencies\n      max_upstream_models: 3\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_max_upstream_dependencies(\n    request: TopRequest,\n    max_upstream_macros: Union[int, None] = None,\n    max_upstream_models: Union[int, None] = None,\n    max_upstream_sources: Union[int, None] = None,\n    model: Union[DbtBouncerModel, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Limit the number of upstream dependencies a model has. Default values are 5 for models, 5 for macros, and 1 for sources.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        max_upstream_macros (Optional[int]): The maximum number of permitted upstream macros. Default: 5\n        max_upstream_models (Optional[int]): The maximum number of permitted upstream models. Default: 5\n        max_upstream_sources (Optional[int]): The maximum number of permitted upstream sources. Default: 1\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_max_upstream_dependencies\n              max_upstream_models: 3\n        ```\n    \"\"\"\n\n    num_upstream_macros = len(list(model.depends_on.macros))\n    num_upstream_models = len([m for m in model.depends_on.nodes if m.split(\".\")[0] == \"model\"])\n    num_upstream_sources = len([m for m in model.depends_on.nodes if m.split(\".\")[0] == \"source\"])\n\n    assert (\n        num_upstream_macros <= max_upstream_macros  # type: ignore[operator]\n    ), f\"`{model.name}` has {num_upstream_macros} upstream macros, which is more than the permitted maximum of {max_upstream_macros}.\"\n    assert (\n        num_upstream_models <= max_upstream_models  # type: ignore[operator]\n    ), f\"`{model.name}` has {num_upstream_models} upstream models, which is more than the permitted maximum of {max_upstream_models}.\"\n    assert (\n        num_upstream_sources <= max_upstream_sources  # type: ignore[operator]\n    ), f\"`{model.name}` has {num_upstream_sources} upstream sources, which is more than the permitted maximum of {max_upstream_sources}.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_names","title":"check_model_names","text":"

Models must have a name that matches the supplied regex.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model_name_pattern str

Regexp the model name must match.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_names\n      include: ^models/intermediate\n      model_name_pattern: ^int_\n    - name: check_model_names\n      include: ^models/staging\n      model_name_pattern: ^stg_\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_names(\n    request: TopRequest,\n    model: Union[DbtBouncerModel, None] = None,\n    model_name_pattern: Union[None, str] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Models must have a name that matches the supplied regex.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model_name_pattern (str): Regexp the model name must match.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_names\n              include: ^models/intermediate\n              model_name_pattern: ^int_\n            - name: check_model_names\n              include: ^models/staging\n              model_name_pattern: ^stg_\n        ```\n    \"\"\"\n\n    assert (\n        re.compile(model_name_pattern.strip()).match(model.name) is not None\n    ), f\"`{model.name}` does not match the supplied regex `{model_name_pattern.strip()})`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_property_file_location","title":"check_model_property_file_location","text":"

Model properties files must follow the guidance provided by dbt here.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the model path. Model paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the model path. Only model paths that match the pattern will be checked.

model DbtBouncerModel

The DbtBouncerModel object to check.

Example(s):

manifest_checks:\n    - name: check_model_property_file_location\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@pytest.mark.iterate_over_models\n@bouncer_check\ndef check_model_property_file_location(\n    request: TopRequest, model: Union[DbtBouncerModel, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Model properties files must follow the guidance provided by dbt [here](https://docs.getdbt.com/best-practices/how-we-structure/1-guide-overview).\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the model path. Model paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the model path. Only model paths that match the pattern will be checked.\n        model (DbtBouncerModel): The DbtBouncerModel object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_property_file_location\n        ```\n    \"\"\"\n\n    expected_substr = (\n        \"_\".join(model.original_file_path.split(\"/\")[1:-1])\n        .replace(\"staging\", \"stg\")\n        .replace(\"intermediate\", \"int\")\n        .replace(\"marts\", \"\")\n    )\n    properties_yml_name = model.patch_path.split(\"/\")[-1]\n\n    assert properties_yml_name.startswith(\n        \"_\"\n    ), f\"The properties file for `{model.name}` (`{properties_yml_name}`) does not start with an underscore.\"\n    assert (\n        expected_substr in properties_yml_name\n    ), f\"The properties file for `{model.name}` (`{properties_yml_name}`) does not contain the expected substring (`{expected_substr}`).\"\n    assert properties_yml_name.endswith(\n        \"__models.yml\"\n    ), f\"The properties file for `{model.name}` (`{properties_yml_name}`) does not end with `__models.yml`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_models.check_model_test_coverage","title":"check_model_test_coverage","text":"

Set the minimum percentage of models that have at least one test.

Receives:

Name Type Description min_model_test_coverage_pct float

The minimum percentage of models that must have at least one test. Default: 100

Example(s):

manifest_checks:\n    - name: check_model_test_coverage\n      min_model_test_coverage_pct: 90\n

Source code in src/dbt_bouncer/checks/manifest/check_models.py
@bouncer_check\ndef check_model_test_coverage(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    tests: List[DbtBouncerModel],\n    min_model_test_coverage_pct: Union[float, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Set the minimum percentage of models that have at least one test.\n\n    Receives:\n        min_model_test_coverage_pct (float): The minimum percentage of models that must have at least one test. Default: 100\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_model_test_coverage\n              min_model_test_coverage_pct: 90\n        ```\n    \"\"\"\n\n    num_models = len(models)\n    models_with_tests = []\n    for model in models:\n        for test in tests:\n            if model.unique_id in test.depends_on.nodes:\n                models_with_tests.append(model.unique_id)\n    num_models_with_tests = len(set(models_with_tests))\n    model_test_coverage_pct = (num_models_with_tests / num_models) * 100\n\n    assert (\n        model_test_coverage_pct >= min_model_test_coverage_pct  # type: ignore[operator]\n    ), f\"Only {model_test_coverage_pct}% of models have at least one test, this is less than the permitted minimum of {min_model_test_coverage_pct}%.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources","title":"check_sources","text":""},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_description_populated","title":"check_source_description_populated","text":"

Sources must have a populated description.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

source DbtBouncerSource

The DbtBouncerSource object to check.

Example(s):

manifest_checks:\n    - name: check_source_description_populated\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_description_populated(\n    request: TopRequest, source: Union[DbtBouncerSource, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Sources must have a populated description.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_description_populated\n        ```\n    \"\"\"\n\n    assert (\n        len(source.description.strip()) > 4\n    ), f\"`{source.source_name}.{source.name}` does not have a populated description.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_freshness_populated","title":"check_source_freshness_populated","text":"

Sources must have a populated freshness.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

source DbtBouncerSource

The DbtBouncerSource object to check.

Example(s):

manifest_checks:\n    - name: check_source_freshness_populated\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_freshness_populated(\n    request: TopRequest, source: Union[DbtBouncerSource, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Sources must have a populated freshness.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_freshness_populated\n        ```\n    \"\"\"\n\n    assert (\n        source.freshness.error_after.count is not None\n        and source.freshness.error_after.period is not None\n    ) or (\n        source.freshness.warn_after.count is not None\n        and source.freshness.warn_after.period is not None\n    ), f\"`{source.source_name}.{source.name}` does not have a populated freshness.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_has_meta_keys","title":"check_source_has_meta_keys","text":"

The meta config for sources must have the specified keys.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

keys NestedDict

A list (that may contain sub-lists) of required keys.

source DbtBouncerSource

The DbtBouncerSource object to check.

Example(s):

manifest_checks:\n    - name: check_source_has_meta_keys\n      keys:\n        - contact:\n            - email\n            - slack\n        - owner\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_has_meta_keys(\n    request,\n    keys: Union[NestedDict, None] = None,\n    source: Union[DbtBouncerSource, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    The `meta` config for sources must have the specified keys.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        keys (NestedDict): A list (that may contain sub-lists) of required keys.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_has_meta_keys\n              keys:\n                - contact:\n                    - email\n                    - slack\n                - owner\n        ```\n    \"\"\"\n\n    missing_keys = find_missing_meta_keys(\n        meta_config=source.meta,\n        required_keys=keys.model_dump(),\n    )\n    assert (\n        missing_keys == []\n    ), f\"`{source.source_name}.{source.name}` is missing the following keys from the `meta` config: {[x.replace('>>', '') for x in missing_keys]}\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_has_tags","title":"check_source_has_tags","text":"

Sources must have the specified tags.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

source DbtBouncerSource

The DbtBouncerSource object to check.

tags List[str]

List of tags to check for.

Example(s):

manifest_checks:\n    - name: check_source_has_tags\n      tags:\n        - tag_1\n        - tag_2\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_has_tags(\n    request: TopRequest,\n    source: Union[DbtBouncerSource, None] = None,\n    tags: Union[None, str] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Sources must have the specified tags.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n        tags (List[str]): List of tags to check for.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_has_tags\n              tags:\n                - tag_1\n                - tag_2\n        ```\n    \"\"\"\n\n    missing_tags = [tag for tag in tags if tag not in source.tags]\n    assert (\n        not missing_tags\n    ), f\"`{source.source_name}.{source.name}` is missing required tags: {missing_tags}.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_loader_populated","title":"check_source_loader_populated","text":"

Sources must have a populated loader.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

source DbtBouncerSource

The DbtBouncerSource object to check.

Example(s):

manifest_checks:\n    - name: check_source_loader_populated\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_loader_populated(\n    request: TopRequest, source: Union[DbtBouncerSource, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Sources must have a populated loader.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_loader_populated\n        ```\n    \"\"\"\n\n    assert (\n        source.loader != \"\"\n    ), f\"`{source.source_name}.{source.name}` does not have a populated loader.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_names","title":"check_source_names","text":"

Sources must have a name that matches the supplied regex.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

source DbtBouncerSource

The DbtBouncerSource object to check.

source_name_pattern str

Regexp the source name must match.

Example(s):

manifest_checks:\n    - name: check_source_names\n      source_name_pattern: >\n        ^[a-z0-9_]*$\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_names(\n    request: TopRequest,\n    source: Union[DbtBouncerSource, None] = None,\n    source_name_pattern: Union[None, str] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Sources must have a name that matches the supplied regex.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n        source_name_pattern (str): Regexp the source name must match.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_names\n              source_name_pattern: >\n                ^[a-z0-9_]*$\n        ```\n    \"\"\"\n\n    assert (\n        re.compile(source_name_pattern.strip()).match(source.name) is not None\n    ), f\"`{source.source_name}.{source.name}` does not match the supplied regex `({source_name_pattern.strip()})`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_not_orphaned","title":"check_source_not_orphaned","text":"

Sources must be referenced in at least one model.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

source DbtBouncerSource

The DbtBouncerSource object to check.

Example(s):

manifest_checks:\n    - name: check_source_not_orphaned\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_not_orphaned(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    source: Union[DbtBouncerSource, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Sources must be referenced in at least one model.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_not_orphaned\n        ```\n    \"\"\"\n\n    num_refs = sum(source.unique_id in model.depends_on.nodes for model in models)\n    assert (\n        num_refs >= 1\n    ), f\"Source `{source.source_name}.{source.name}` is orphaned, i.e. not referenced by any model.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_property_file_location","title":"check_source_property_file_location","text":"

Source properties files must follow the guidance provided by dbt here.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

source DbtBouncerSource

The DbtBouncerSource object to check.

Example(s):

manifest_checks:\n    - name: check_source_property_file_location\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_property_file_location(\n    request: TopRequest, source: Union[DbtBouncerSource, None] = None, **kwargs\n) -> None:\n    \"\"\"\n    Source properties files must follow the guidance provided by dbt [here](https://docs.getdbt.com/best-practices/how-we-structure/1-guide-overview).\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_property_file_location\n        ```\n    \"\"\"\n\n    path_cleaned = source.original_file_path.replace(\"models/staging\", \"\")\n    expected_substring = \"_\".join(path_cleaned.split(\"/\")[:-1])\n\n    assert path_cleaned.split(\"/\")[-1].startswith(\n        \"_\"\n    ), f\"The properties file for `{source.source_name}.{source.name}` (`{path_cleaned}`) does not start with an underscore.\"\n    assert (\n        expected_substring in path_cleaned\n    ), f\"The properties file for `{source.source_name}.{source.name}` (`{path_cleaned}`) does not contain the expected substring (`{expected_substring}`).\"\n    assert path_cleaned.split(\"/\")[-1].endswith(\n        \"__sources.yml\"\n    ), f\"The properties file for `{source.source_name}.{source.name}` (`{path_cleaned}`) does not end with `__sources.yml`.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_used_by_models_in_same_directory","title":"check_source_used_by_models_in_same_directory","text":"

Sources can only be referenced by models that are located in the same directory where the source is defined.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

source DbtBouncerSource

The DbtBouncerSource object to check.

Example(s):

manifest_checks:\n    - name: check_source_used_by_models_in_same_directory\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_used_by_models_in_same_directory(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    source: Union[DbtBouncerSource, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Sources can only be referenced by models that are located in the same directory where the source is defined.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_used_by_models_in_same_directory\n        ```\n    \"\"\"\n\n    reffed_models_not_in_same_dir = []\n    for model in models:\n        if (\n            source.unique_id in model.depends_on.nodes\n            and model.original_file_path.split(\"/\")[:-1]\n            != source.original_file_path.split(\"/\")[:-1]\n        ):\n            reffed_models_not_in_same_dir.append(model.unique_id.split(\".\")[0])\n\n    assert (\n        len(reffed_models_not_in_same_dir) == 0\n    ), f\"Source `{source.source_name}.{source.name}` is referenced by models defined in a different directory: {reffed_models_not_in_same_dir}\"\n
"},{"location":"checks/checks_manifest/#manifest.check_sources.check_source_used_by_only_one_model","title":"check_source_used_by_only_one_model","text":"

Each source can be referenced by a maximum of one model.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.

source DbtBouncerSource

The DbtBouncerSource object to check.

Example(s):

manifest_checks:\n    - name: check_source_used_by_only_one_model\n

Source code in src/dbt_bouncer/checks/manifest/check_sources.py
@pytest.mark.iterate_over_sources\n@bouncer_check\ndef check_source_used_by_only_one_model(\n    models: List[DbtBouncerModel],\n    request: TopRequest,\n    source: Union[DbtBouncerSource, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Each source can be referenced by a maximum of one model.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Source paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the source path (i.e the .yml file where the source is configured). Only source paths that match the pattern will be checked.\n        source (DbtBouncerSource): The DbtBouncerSource object to check.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_source_used_by_only_one_model\n        ```\n    \"\"\"\n\n    num_refs = sum(source.unique_id in model.depends_on.nodes for model in models)\n    assert (\n        num_refs <= 1\n    ), f\"Source `{source.source_name}.{source.name}` is referenced by more than one model.\"\n
"},{"location":"checks/checks_manifest/#manifest.check_unit_tests","title":"check_unit_tests","text":""},{"location":"checks/checks_manifest/#manifest.check_unit_tests.check_unit_test_expect_format","title":"check_unit_test_expect_format","text":"

Unit tests can only use the specified formats.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the unit test path (i.e the .yml file where the unit test is configured). Unit test paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the unit test path (i.e the .yml file where the unit test is configured). Only unit test paths that match the pattern will be checked.

permitted_formats Optional[List[Literal['csv', 'dict', 'sql']]]

A list of formats that are allowed to be used for expect input in a unit test.

unit_test UnitTests

The UnitTests object to check.

Warning

This check is only supported for dbt 1.8.0 and above.

Example(s):

manifest_checks:\n    - name: check_unit_test_expect_format\n      permitted_formats:\n        - csv\n

Source code in src/dbt_bouncer/checks/manifest/check_unit_tests.py
@pytest.mark.iterate_over_unit_tests\n@bouncer_check\ndef check_unit_test_expect_format(\n    manifest_obj: DbtBouncerManifest,\n    request: TopRequest,\n    permitted_formats: Union[List[Literal[\"csv\", \"dict\", \"sql\"]], None] = None,\n    unit_test: Union[UnitTests, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Unit tests can only use the specified formats.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the unit test path (i.e the .yml file where the unit test is configured). Unit test paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the unit test path (i.e the .yml file where the unit test is configured). Only unit test paths that match the pattern will be checked.\n        permitted_formats (Optional[List[Literal[\"csv\", \"dict\", \"sql\"]]]): A list of formats that are allowed to be used for `expect` input in a unit test.\n        unit_test (UnitTests): The UnitTests object to check.\n\n    !!! warning\n\n        This check is only supported for dbt 1.8.0 and above.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_unit_test_expect_format\n              permitted_formats:\n                - csv\n        ```\n    \"\"\"\n\n    if semver.Version.parse(manifest_obj.manifest.metadata.dbt_version) >= \"1.8.0\":\n        assert (\n            unit_test.expect.format.value in permitted_formats  # type: ignore[operator]\n        ), f\"Unit test `{unit_test.name}` has an `expect` format that is not permitted. Permitted formats are: {permitted_formats}.\"\n    else:\n        logging.warning(\n            \"The `check_unit_test_expect_format` check is only supported for dbt 1.8.0 and above.\"\n        )\n
"},{"location":"checks/checks_manifest/#manifest.check_unit_tests.check_unit_test_given_formats","title":"check_unit_test_given_formats","text":"

Unit tests can only use the specified formats.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the unit test path (i.e the .yml file where the unit test is configured). Unit test paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the unit test path (i.e the .yml file where the unit test is configured). Only unit test paths that match the pattern will be checked.

permitted_formats Optional[List[Literal['csv', 'dict', 'sql']]]

A list of formats that are allowed to be used for given inputs in a unit test.

unit_test UnitTests

The UnitTests object to check.

Warning

This check is only supported for dbt 1.8.0 and above.

Example(s):

manifest_checks:\n    - name: check_unit_test_given_formats\n      permitted_formats:\n        - csv\n

Source code in src/dbt_bouncer/checks/manifest/check_unit_tests.py
@pytest.mark.iterate_over_unit_tests\n@bouncer_check\ndef check_unit_test_given_formats(\n    manifest_obj: DbtBouncerManifest,\n    request: TopRequest,\n    permitted_formats: Union[List[Literal[\"csv\", \"dict\", \"sql\"]], None] = None,\n    unit_test: Union[UnitTests, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Unit tests can only use the specified formats.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the unit test path (i.e the .yml file where the unit test is configured). Unit test paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the unit test path (i.e the .yml file where the unit test is configured). Only unit test paths that match the pattern will be checked.\n        permitted_formats (Optional[List[Literal[\"csv\", \"dict\", \"sql\"]]]): A list of formats that are allowed to be used for `given` inputs in a unit test.\n        unit_test (UnitTests): The UnitTests object to check.\n\n    !!! warning\n\n        This check is only supported for dbt 1.8.0 and above.\n\n    Example(s):\n        ```yaml\n        manifest_checks:\n            - name: check_unit_test_given_formats\n              permitted_formats:\n                - csv\n        ```\n    \"\"\"\n\n    if semver.Version.parse(manifest_obj.manifest.metadata.dbt_version) >= \"1.8.0\":\n        given_formats = [i.format.value for i in unit_test.given]\n        assert all(\n            e in permitted_formats for e in given_formats  # type: ignore[operator]\n        ), f\"Unit test `{unit_test.name}` has given formats which are not permitted. Permitted formats are: {permitted_formats}.\"\n    else:\n        logging.warning(\n            \"The `check_unit_test_given_formats` check is only supported for dbt 1.8.0 and above.\"\n        )\n
"},{"location":"checks/checks_run_results/","title":"Run Results Checks","text":"

Note

The below checks require both manifest.json and run_results.json to be present.

"},{"location":"checks/checks_run_results/#run_results.check_run_results","title":"check_run_results","text":""},{"location":"checks/checks_run_results/#run_results.check_run_results.check_run_results_max_gigabytes_billed","title":"check_run_results_max_gigabytes_billed","text":"

Each result can have a maximum number of gigabytes billed.

Note

Note that this check only works for the dbt-bigquery adapter.

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the resource path. Resource paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the resource path. Only resource paths that match the pattern will be checked.

max_gigabytes_billed float

The maximum gigabytes billed allowed for a node.

run_result DbtBouncerResult

The DbtBouncerResult object to check.

Example(s):

run_results_checks:\n    - name: check_run_results_max_gigabytes_billed\n      max_gigabytes_billed: 100\n

Source code in src/dbt_bouncer/checks/run_results/check_run_results.py
@pytest.mark.iterate_over_run_results\n@bouncer_check\ndef check_run_results_max_gigabytes_billed(\n    request: TopRequest,\n    max_gigabytes_billed: Union[float, None] = None,\n    run_result: Union[DbtBouncerResult, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Each result can have a maximum number of gigabytes billed.\n\n    !!! note\n\n        Note that this check only works for the `dbt-bigquery` adapter.\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the resource path. Resource paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the resource path. Only resource paths that match the pattern will be checked.\n        max_gigabytes_billed (float): The maximum gigabytes billed allowed for a node.\n        run_result (DbtBouncerResult): The DbtBouncerResult object to check.\n\n    Example(s):\n        ```yaml\n        run_results_checks:\n            - name: check_run_results_max_gigabytes_billed\n              max_gigabytes_billed: 100\n        ```\n    \"\"\"\n\n    try:\n        gigabytes_billed = run_result.adapter_response[\"bytes_billed\"] / (1000**3)\n    except KeyError:\n        raise RuntimeError(\n            \"`bytes_billed` not found in adapter response. Are you using the `dbt-bigquery` adapter?\"\n        )\n\n    assert (\n        gigabytes_billed < max_gigabytes_billed\n    ), f\"`{run_result.unique_id.split('.')[-2]}` results in ({gigabytes_billed} billed bytes, this is greater than permitted ({max_gigabytes_billed}).\"\n
"},{"location":"checks/checks_run_results/#run_results.check_run_results.check_run_results_max_execution_time","title":"check_run_results_max_execution_time","text":"

Each result can take a maximum duration (seconds).

Receives:

Name Type Description exclude Optional[str]

Regex pattern to match the resource path. Resource paths that match the pattern will not be checked.

include Optional[str]

Regex pattern to match the resource path. Only resource paths that match the pattern will be checked.

max_execution_time_seconds float

The maximum execution time (seconds) allowed for a node.

run_result DbtBouncerResult

The DbtBouncerResult object to check.

Example(s):

run_results_checks:\n    - name: check_run_results_max_execution_time\n      max_execution_time_seconds: 60\n
run_results_checks:\n    - name: check_run_results_max_execution_time\n      include: ^models/staging # Not a good idea, here for demonstration purposes only\n      max_execution_time_seconds: 10\n

Source code in src/dbt_bouncer/checks/run_results/check_run_results.py
@pytest.mark.iterate_over_run_results\n@bouncer_check\ndef check_run_results_max_execution_time(\n    request: TopRequest,\n    max_execution_time_seconds: Union[float, None] = None,\n    run_result: Union[DbtBouncerResult, None] = None,\n    **kwargs,\n) -> None:\n    \"\"\"\n    Each result can take a maximum duration (seconds).\n\n    Receives:\n        exclude (Optional[str]): Regex pattern to match the resource path. Resource paths that match the pattern will not be checked.\n        include (Optional[str]): Regex pattern to match the resource path. Only resource paths that match the pattern will be checked.\n        max_execution_time_seconds (float): The maximum execution time (seconds) allowed for a node.\n        run_result (DbtBouncerResult): The DbtBouncerResult object to check.\n\n    Example(s):\n        ```yaml\n        run_results_checks:\n            - name: check_run_results_max_execution_time\n              max_execution_time_seconds: 60\n        ```\n        ```yaml\n        run_results_checks:\n            - name: check_run_results_max_execution_time\n              include: ^models/staging # Not a good idea, here for demonstration purposes only\n              max_execution_time_seconds: 10\n        ```\n    \"\"\"\n\n    assert (\n        run_result.execution_time <= max_execution_time_seconds\n    ), f\"`{run_result.unique_id.split('.')[-1]}` has an execution time ({run_result.execution_time} greater than permitted ({max_execution_time_seconds}s).\"\n
"}]} \ No newline at end of file