Skip to content

Commit dc98d01

Browse files
authored
Feat/83 python api (cont) (#94)
* feat: python api adding (cont) * chore: test api * chore: add tests * chore: cq * docs: update readme [skip ci]
1 parent 726c061 commit dc98d01

File tree

10 files changed

+169
-42
lines changed

10 files changed

+169
-42
lines changed

README.md

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ dbterd --version
2121
## Quick examine with existing samples
2222

2323
<details>
24-
<summary>Click me</summary>
24+
<summary>Play with CLI</summary>
2525

2626
```bash
2727
# select all models in dbt_resto
@@ -53,6 +53,58 @@ dbterd --version
5353

5454
</details>
5555

56+
<details>
57+
<summary>Play with Python API (whole ERD)</summary>
58+
59+
```python
60+
from dbterd.api import DbtErd
61+
62+
erd = DbtErd().get_erd()
63+
print("erd (dbml):", erd)
64+
65+
erd = DbtErd(target="mermaid").get_erd()
66+
print("erd (mermaid):", erd)
67+
```
68+
69+
</details>
70+
71+
72+
<details>
73+
<summary>Play with Python API (1 model's ERD)</summary>
74+
75+
```python
76+
from dbterd.api import DbtErd
77+
78+
dim_prize_erd = DbtErd(target="mermaid").get_model_erd(
79+
node_unique_id="model.dbt_resto.dim_prize"
80+
)
81+
print("erd of dim_date (mermaid):", dim_prize_erd)
82+
```
83+
84+
Here is the output:
85+
86+
```mermaid
87+
erDiagram
88+
"MODEL.DBT_RESTO.DIM_PRIZE" {
89+
varchar prize_key
90+
nvarchar prize_name
91+
int prize_order
92+
}
93+
"MODEL.DBT_RESTO.FACT_RESULT" {
94+
varchar fact_result_key
95+
varchar box_key
96+
varchar prize_key
97+
date date_key
98+
int no_of_won
99+
float prize_value
100+
float prize_paid
101+
int is_prize_taken
102+
}
103+
"MODEL.DBT_RESTO.FACT_RESULT" }|--|| "MODEL.DBT_RESTO.DIM_PRIZE": prize_key
104+
```
105+
106+
</details>
107+
56108
## Quick DEMO
57109

58110
Check [Quick Demo](https://dbterd.datnguyen.de/latest/nav/guide/targets/generate-dbml.html) out! And, following is the sample result using `dbdocs`:

dbterd/adapters/base.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import os
22
from pathlib import Path
3+
from types import NotImplementedType
34
from typing import List, Tuple
45

56
import click
@@ -238,7 +239,10 @@ def __run_by_strategy(
238239
if not kwargs.get("api"):
239240
self.__save_result(path=kwargs.get("output"), data=result)
240241

241-
return result
242+
if type(result) == NotImplementedType:
243+
return result
244+
245+
return result[1]
242246

243247
def __run_metadata_by_strategy(self, **kwargs) -> Tuple[List[Table], List[Ref]]:
244248
"""Metadata - Read artifacts and export the diagram file following the target"""
@@ -250,4 +254,7 @@ def __run_metadata_by_strategy(self, **kwargs) -> Tuple[List[Table], List[Ref]]:
250254
if not kwargs.get("api"):
251255
self.__save_result(path=kwargs.get("output"), data=result)
252256

253-
return result
257+
if type(result) == NotImplementedType:
258+
return result
259+
260+
return result[1]

dbterd/api/__init__.py

Lines changed: 33 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,54 @@
1+
import logging
2+
from pathlib import Path
13
from typing import List, Tuple
24

35
from click import Command, Context
46

57
from dbterd import default
68
from dbterd.adapters.base import Executor
79
from dbterd.adapters.meta import Ref, Table
10+
from dbterd.helpers.log import logger
11+
12+
logger.setLevel(logging.WARNING) # hide log
813

914

1015
class DbtErd:
1116
"""
1217
dbt ERD official API functions.
1318
1419
15-
Usage:
20+
**Usage**:
1621
17-
- Get a whole ERD:
18-
```python
19-
from dbterd.api import DbtErd
20-
erd = DbtErd().get_erd()
21-
```
22+
## Get a whole ERD
2223
23-
- Get a whole ERD given all models attached to `my_exposure_name`:
24-
```python
25-
from dbterd.api import DbtErd
26-
erd = DbtErd(select="exposure:my_exposure_name").get_erd()
27-
```
28-
See the
29-
[Selection](https://dbterd.datnguyen.de/latest/nav/guide/cli-references.html#dbterd-run-select-s)
30-
page for more details.
24+
```python
25+
from dbterd.api import DbtErd
26+
erd = DbtErd().get_erd()
27+
```
3128
32-
- Get a model (named `model.jaffle_shop.my_model`)'s ERD:
33-
```python
34-
from dbterd.api import DbtErd
35-
erd = DbtErd().get_model_erd(s
36-
node_fqn="model.jaffle_shop.my_model"
37-
)
38-
```
29+
## Get a whole ERD given all models attached to `my_exposure_name`
30+
31+
```python
32+
from dbterd.api import DbtErd
33+
erd = DbtErd(select="exposure:my_exposure_name").get_erd()
34+
```
35+
See the
36+
[Selection](https://dbterd.datnguyen.de/latest/nav/guide/cli-references.html#dbterd-run-select-s)
37+
page for more details.
38+
39+
## Get a model (named `model.jaffle_shop.my_model`)'s ERD
40+
41+
```python
42+
from dbterd.api import DbtErd
43+
erd = DbtErd().get_model_erd(s
44+
node_fqn="model.jaffle_shop.my_model"
45+
)
46+
```
3947
"""
4048

4149
def __init__(self, **kwargs) -> None:
4250
"""Initialize the main Executor given similar input CLI parameters"""
51+
4352
self.params: dict = kwargs
4453
"""
4554
Mimic CLI params with overriding `api = True`.\n
@@ -67,11 +76,13 @@ def __set_params_default_if_not_specified(self) -> None:
6776
self.params["resource_type"] = self.params.get(
6877
"resource_type", default.default_resource_types()
6978
)
70-
self.params["algo"] = self.params.get("algo", default.deafult_algo())
79+
self.params["algo"] = self.params.get("algo", default.default_algo())
7180
self.params["entity_name_format"] = self.params.get(
7281
"entity_name_format", default.default_entity_name_format()
7382
)
7483
self.params["omit_columns"] = self.params.get("omit_columns", False)
84+
self.params["artifacts_dir"] = self.params.get("artifacts_dir", Path.cwd())
85+
self.params["target"] = self.params.get("target", default.default_target())
7586

7687
def get_erd(self) -> Tuple[List[Table], List[Ref]]:
7788
"""Generate ERD code for a whole project

dbterd/cli/params.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def common_params(func):
4343
"--algo",
4444
"-a",
4545
help="Specified algorithm in the way to detect diagram connectors",
46-
default=default.deafult_algo(),
46+
default=default.default_algo(),
4747
show_default=True,
4848
type=click.STRING,
4949
)

dbterd/default.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def default_target() -> str:
1414
return "dbml"
1515

1616

17-
def deafult_algo() -> str:
17+
def default_algo() -> str:
1818
return "test_relationship"
1919

2020

samples/dbtresto/erd.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
from dbterd.api import DbtErd
2+
3+
erd = DbtErd().get_erd()
4+
print("erd (dbml):", erd)
5+
erd = DbtErd(target="mermaid").get_erd()
6+
print("erd (mermaid):", erd)
7+
8+
print("===============")
9+
print("===============")
10+
fact_number_erd = DbtErd(target="mermaid").get_model_erd(
11+
node_unique_id="model.dbt_resto.fact_number"
12+
)
13+
print("erd of fact_number (mermaid):", fact_number_erd)
14+
15+
print("===============")
16+
print("===============")
17+
dim_prize_erd = DbtErd(target="mermaid").get_model_erd(
18+
node_unique_id="model.dbt_resto.dim_prize"
19+
)
20+
print("erd of dim_date (mermaid):", dim_prize_erd)

tests/unit/adapters/test_base.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from pathlib import Path
2+
from types import NotImplementedType
23
from unittest import mock
34

45
import click
@@ -27,6 +28,18 @@ def test___run_metadata_by_strategy(self, mock_query_erd_data, mock_save_result)
2728
mock_query_erd_data.assert_called_once()
2829
mock_save_result.assert_called_once()
2930

31+
@mock.patch("dbterd.adapters.base.DbtCloudMetadata.query_erd_data")
32+
@mock.patch("dbterd.adapters.base.Executor._Executor__save_result")
33+
def test___run_metadata_by_strategy_with_not_implemented_algo(
34+
self, mock_query_erd_data, mock_save_result
35+
):
36+
result = Executor(
37+
ctx=click.Context(command=click.BaseCommand("dummy"))
38+
)._Executor__run_metadata_by_strategy(target="dbml", algo="notfound")
39+
assert type(result) == NotImplementedType
40+
mock_query_erd_data.assert_called_once()
41+
mock_save_result.assert_called_once()
42+
3043
@mock.patch("builtins.open")
3144
def test___save_result(self, mock_open):
3245
Executor(
@@ -260,7 +273,7 @@ def test__run_by_strategy__for_api_simple(
260273
mock_parent.attach_mock(
261274
mock_set_single_node_selection, "mock_set_single_node_selection"
262275
)
263-
mock_dbml_run.return_value = dict(i="irr")
276+
mock_dbml_run.return_value = ("irr", dict(i="irr"))
264277
mock_parent.attach_mock(mock_dbml_run, "mock_dbml_run")
265278

266279
assert worker._Executor__run_by_strategy(node_unique_id="irr") == dict(i="irr")

tests/unit/api/test_api.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
from pathlib import Path
2+
from unittest import mock
3+
4+
from dbterd import default
5+
from dbterd.api import DbtErd
6+
7+
8+
class TestDbtErd:
9+
@mock.patch("dbterd.adapters.base.Executor.run")
10+
def test_get_erd(self, mock_executor_run):
11+
mock_executor_run.return_value = "expected-result"
12+
assert DbtErd().get_erd() == "expected-result"
13+
14+
@mock.patch("dbterd.adapters.base.Executor.run")
15+
def test_get_model_erd(self, mock_executor_run):
16+
mock_executor_run.return_value = "expected-result"
17+
assert DbtErd().get_model_erd(node_unique_id="any") == "expected-result"
18+
19+
def test_init_default(self):
20+
actual = DbtErd()
21+
actual_dict = dict(vars(actual))
22+
del actual_dict["executor"]
23+
assert actual_dict == dict(
24+
params=dict(
25+
api=True,
26+
select=[],
27+
exclude=[],
28+
resource_type=default.default_resource_types(),
29+
algo=default.default_algo(),
30+
entity_name_format=default.default_entity_name_format(),
31+
omit_columns=False,
32+
artifacts_dir=Path.cwd(),
33+
target=default.default_target(),
34+
)
35+
)
36+
assert actual.executor.ctx.command.name == "run"

tests/unit/test_api.py

Lines changed: 0 additions & 12 deletions
This file was deleted.

tests/unit/test_default.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@ def test_default_target(self, target):
1717
assert default.default_target() == target
1818

1919
@pytest.mark.parametrize("algo", [("test_relationship")])
20-
def test_deafult_algo(self, algo):
21-
assert default.deafult_algo() == algo
20+
def test_default_algo(self, algo):
21+
assert default.default_algo() == algo

0 commit comments

Comments
 (0)