Skip to content

Commit 166f031

Browse files
authored
Dbt subdirectory (#36)
Reading manifest file through dbt project
1 parent eb2bf14 commit 166f031

File tree

12 files changed

+131
-114
lines changed

12 files changed

+131
-114
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1515

1616
### Changed
1717

18+
### Breaking Changes
19+
* Path to dbt project.yml file is provided instead of manifest.json
20+
1821
### Fixed
1922
* Fixed generation of CTE names from references with hyphens
23+
* Fixed query paths if dbt project is in subdirectory
2024

2125
## [0.5.0]
2226

docs/dbt.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,14 @@ This guide will provide a quick start on how to use SQLMock with dbt (data build
1111

1212
## Configuration
1313

14-
### Setting the dbt Manifest Path
14+
### Setting the dbt Project Path
1515

16-
Initialize your testing environment by setting the global path to your dbt manifest file:
16+
Initialize your testing environment by setting the global path to your dbt project file:
1717

1818
```python
1919
from sql_mock.config import SQLMockConfig
2020

21-
SQLMockConfig.set_dbt_manifest_path('/path/to/your/dbt/manifest.json')
21+
SQLMockConfig.set_dbt_project_path('/path/to/your/dbt_project.yml')
2222
```
2323

2424
## Creating Table Mocks

examples/dbt/test_example.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
from sql_mock.config import SQLMockConfig
44
from sql_mock.dbt import dbt_model_meta, dbt_seed_meta, dbt_source_meta
55

6-
SQLMockConfig.set_dbt_manifest_path("./tests/resources/dbt/dbt_manifest.json")
6+
SQLMockConfig.set_dbt_project_path("./tests/resources/dbt/dbt_project.yml")
77

88

99
# NOTE: The Source and Seed classes will not be used in the example test. They are only here for demonstration purpose.

poetry.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ python = "^3.9"
4141
jinja2 = "^3.1.2"
4242
pydantic-settings = "^2.0.3"
4343
sqlglot = "^20.5.0"
44+
pyyaml = "^6.0.1"
4445

4546
# Clickhouse specific
4647
clickhouse-driver = {extras = ["numpy"], version = "^0.2.6", optional = true}

src/sql_mock/config.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
class SQLMockConfig:
2-
_dbt_manifest_path = None
2+
_dbt_project_path = None
33

44
@classmethod
5-
def set_dbt_manifest_path(cls, path: str):
6-
cls._dbt_manifest_path = path
5+
def set_dbt_project_path(cls, path: str):
6+
cls._dbt_project_path = path
77

88
@classmethod
9-
def get_dbt_manifest_path(cls):
10-
if cls._dbt_manifest_path is None:
11-
raise ValueError("DBT manifest path is not set. Please set it using set_dbt_manifest_path()")
12-
return cls._dbt_manifest_path
9+
def get_dbt_project_path(cls):
10+
if cls._dbt_project_path is None:
11+
raise ValueError("DBT project path is not set. Please set it using set_dbt_project_path()")
12+
return cls._dbt_project_path

src/sql_mock/dbt.py

Lines changed: 38 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import json
2+
import os
23
from typing import TYPE_CHECKING
34

5+
import yaml
6+
47
from sql_mock.config import SQLMockConfig
58
from sql_mock.helpers import parse_table_refs, validate_input_mocks
69
from sql_mock.table_mocks import TableMockMeta
@@ -10,44 +13,53 @@
1013
from sql_mock.table_mocks import BaseTableMock
1114

1215

13-
def _get_model_metadata_from_dbt_manifest(manifest_path: str, model_name: str) -> dict:
16+
def _get_manifest_from_project_file(project_path: str) -> dict:
17+
with open(project_path, "r") as f:
18+
dbt_project = yaml.safe_load(f)
19+
target_path = dbt_project["target-path"]
20+
project_dir = os.path.dirname(project_path)
21+
with open(os.path.join(project_dir, target_path, "manifest.json"), "r") as file:
22+
manifest = json.load(file)
23+
return manifest
24+
25+
26+
def _get_model_metadata(project_path: str, model_name: str) -> dict:
1427
"""
1528
Extracts the rendered SQL query for a specified model from the dbt manifest file.
1629
1730
Args:
18-
manifest_path (str): Path to the dbt manifest.json file.
31+
project_path (str): Path to the dbt_project.yml file.
1932
model_name (str): Name of the dbt model.
2033
2134
Returns:
2235
dict: Dictionary of metadata from dbt (path to compiled sql query and table ref)
2336
"""
24-
with open(manifest_path, "r") as file:
25-
manifest = json.load(file)
37+
manifest = _get_manifest_from_project_file(project_path)
38+
project_dir = os.path.dirname(project_path)
2639

2740
for node in manifest["nodes"].values():
2841
if node["resource_type"] == "model" and node["name"] == model_name:
2942
return {
30-
"query_path": node["compiled_path"],
43+
"query_path": os.path.join(project_dir, node["compiled_path"]),
3144
"table_ref": node["relation_name"],
3245
}
3346

3447
raise ValueError(f"Model '{model_name}' not found in dbt manifest.")
3548

3649

37-
def _get_source_metadata_from_dbt_manifest(manifest_path: str, source_name: str, table_name: str) -> dict:
50+
def _get_source_metadata(project_path: str, source_name: str, table_name: str) -> dict:
3851
"""
3952
Extracts the table metadata for dbt source from the manifest file.
4053
4154
Args:
42-
manifest_path (str): Path to the dbt manifest.json file.
55+
project_path (str): Path to the dbt_project.yml file.
4356
source_name (str): Name of the dbt source.
4457
table_name (str): Name of the table in the dbt source.
4558
4659
Returns:
4760
dict: Dictionary of metadata from dbt
4861
"""
49-
with open(manifest_path, "r") as file:
50-
manifest = json.load(file)
62+
manifest = _get_manifest_from_project_file(project_path)
5163

5264
for node in manifest["sources"].values():
5365
if (
@@ -62,19 +74,18 @@ def _get_source_metadata_from_dbt_manifest(manifest_path: str, source_name: str,
6274
raise ValueError(f"Source '{source_name}' not found in dbt manifest.")
6375

6476

65-
def _get_seed_metadata_from_dbt_manifest(manifest_path: str, seed_name: str) -> dict:
77+
def _get_seed_metadata(project_path: str, seed_name: str) -> dict:
6678
"""
6779
Extracts the table metadata for dbt seed from the manifest file.
6880
6981
Args:
70-
manifest_path (str): Path to the dbt manifest.json file.
82+
project_path (str): Path to the dbt_project.yml file.
7183
seed_name (str): Name of the dbt seed.
7284
7385
Returns:
7486
dict: Dictionary of metadata from dbt
7587
"""
76-
with open(manifest_path, "r") as file:
77-
manifest = json.load(file)
88+
manifest = _get_manifest_from_project_file(project_path)
7889

7990
for node in manifest["nodes"].values():
8091
if node["resource_type"] == "seed" and node["name"] == seed_name:
@@ -85,20 +96,20 @@ def _get_seed_metadata_from_dbt_manifest(manifest_path: str, seed_name: str) ->
8596
raise ValueError(f"Seed '{seed_name}' not found in dbt manifest.")
8697

8798

88-
def dbt_model_meta(model_name: str, manifest_path: str = None, default_inputs: ["BaseTableMock"] = None):
99+
def dbt_model_meta(model_name: str, project_path: str = None, default_inputs: ["BaseTableMock"] = None):
89100
"""
90101
Decorator that is used to define TableMock metadata for dbt models.
91102
92103
Args:
93104
model_name (string) : Name of the dbt model
94-
manifest_path (string): Path to the dbt manifest file
105+
project_path (string): Path to the dbt manifest file
95106
default_inputs: List of default input mock instances that serve as default input if no other instance of that class is provided.
96107
"""
97108

98109
def decorator(cls):
99-
path = manifest_path or SQLMockConfig.get_dbt_manifest_path()
110+
path = project_path or SQLMockConfig.get_dbt_project_path()
100111

101-
dbt_meta = _get_model_metadata_from_dbt_manifest(manifest_path=path, model_name=model_name)
112+
dbt_meta = _get_model_metadata(project_path=path, model_name=model_name)
102113

103114
parsed_query = ""
104115
with open(dbt_meta["query_path"]) as f:
@@ -118,23 +129,23 @@ def decorator(cls):
118129

119130

120131
def dbt_source_meta(
121-
source_name: str, table_name: str, manifest_path: str = None, default_inputs: ["BaseTableMock"] = None
132+
source_name: str, table_name: str, project_path: str = None, default_inputs: ["BaseTableMock"] = None
122133
):
123134
"""
124135
Decorator that is used to define TableMock metadata for dbt sources.
125136
126137
Args:
127138
source_name (string) : Name of source
128139
table_name (string): Name of the table in the source
129-
manifest_path (string): Path to the dbt manifest file
140+
project_path (string): Path to the dbt manifest file
130141
default_inputs: List of default input mock instances that serve as default input if no other instance of that class is provided.
131142
"""
132143

133144
def decorator(cls):
134-
path = manifest_path or SQLMockConfig.get_dbt_manifest_path()
145+
path = project_path or SQLMockConfig.get_dbt_project_path()
135146

136-
dbt_meta = _get_source_metadata_from_dbt_manifest(
137-
manifest_path=path, source_name=source_name, table_name=table_name
147+
dbt_meta = _get_source_metadata(
148+
project_path=path, source_name=source_name, table_name=table_name
138149
)
139150

140151
if default_inputs:
@@ -149,21 +160,21 @@ def decorator(cls):
149160
return decorator
150161

151162

152-
def dbt_seed_meta(seed_name: str, manifest_path: str = None, default_inputs: ["BaseTableMock"] = None):
163+
def dbt_seed_meta(seed_name: str, project_path: str = None, default_inputs: ["BaseTableMock"] = None):
153164
"""
154165
Decorator that is used to define TableMock metadata for dbt sources.
155166
156167
Args:
157168
seed_name (string) : Name of the dbt seed
158-
manifest_path (string): Path to the dbt manifest file
169+
project_path (string): Path to the dbt manifest file
159170
default_inputs: List of default input mock instances that serve as default input if no other instance of that class is provided.
160171
"""
161172

162173
def decorator(cls):
163-
path = manifest_path or SQLMockConfig.get_dbt_manifest_path()
174+
path = project_path or SQLMockConfig.get_dbt_project_path()
164175

165-
dbt_meta = _get_seed_metadata_from_dbt_manifest(
166-
manifest_path=path,
176+
dbt_meta = _get_seed_metadata(
177+
project_path=path,
167178
seed_name=seed_name,
168179
)
169180

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
target-path: "dbt_target"

0 commit comments

Comments
 (0)