Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Support dbt 1.7 #929

Merged
merged 5 commits into from Nov 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/dbt-compatible-tests.yaml
Expand Up @@ -16,7 +16,7 @@ jobs:
fail-fast: false
matrix:
version: [ '3.8', '3.9', '3.10', '3.11' ]
dbt: [ ">=1.3,<1.4", ">=1.4,<1.5", ">=1.5,<1.6", ">=1.6,<1.7" ]
dbt: [ ">=1.3,<1.4", ">=1.4,<1.5", ">=1.5,<1.6", ">=1.6,<1.7", ">=1.7,<1.8" ]

steps:
- uses: actions/checkout@v2
Expand Down
18 changes: 10 additions & 8 deletions piperider_cli/dbt/list_task.py
Expand Up @@ -34,7 +34,7 @@
register_adapter(runtime_config)

v = dbt_version
if v == '1.5' or v == '1.6':
if v == '1.5' or v == '1.6' or v == '1.7':

Check warning on line 37 in piperider_cli/dbt/list_task.py

View check run for this annotation

Codecov / codecov/patch

piperider_cli/dbt/list_task.py#L37

Added line #L37 was not covered by tests
return ManifestLoader.get_full_manifest(
runtime_config, write_perf_info=False
)
Expand All @@ -56,7 +56,7 @@
if v == '1.5':
return _load_manifest_version_15(manifest)

if v == '1.6':
if v == '1.6' or v == '1.7':
return _load_manifest_version_16(manifest)

raise NotImplementedError(f'dbt-core version: {v} is not supported')
Expand Down Expand Up @@ -114,12 +114,7 @@
return int(match.group(1))
raise ValueError("Manifest doesn't have schema version")

import dbt.contracts.graph.manifest
origin_function = dbt.contracts.graph.manifest.get_manifest_schema_version
dbt.contracts.graph.manifest.get_manifest_schema_version = patched_get_manifest_schema_version

result = WritableManifest.upgrade_schema_version(data)
dbt.contracts.graph.manifest.get_manifest_schema_version = origin_function
return result


Expand Down Expand Up @@ -265,7 +260,7 @@
setattr(flags, "target", None)

v = dbt_version
if v == '1.5' or v == '1.6':
if v == '1.5' or v == '1.6' or v == '1.7':

Check warning on line 263 in piperider_cli/dbt/list_task.py

View check run for this annotation

Codecov / codecov/patch

piperider_cli/dbt/list_task.py#L263

Added line #L263 was not covered by tests
return _get_v15_runtime_config(flags)
elif v == '1.4':
return _get_v14_runtime_config(flags)
Expand Down Expand Up @@ -362,6 +357,13 @@
if has_field('packages_specified_path'):
data['packages_specified_path'] = "packages.yml"

# dbt 1.7
if has_field('semantic_models'):
data['semantic_models'] = {}

if has_field('saved_queries'):
data['saved_queries'] = {}

super().__init__(args=None, **data)

def validate(self):
Expand Down
39 changes: 29 additions & 10 deletions piperider_cli/dbtutil.py
Expand Up @@ -468,20 +468,39 @@
primary_entity = None
metric_filter = []
if metric.get('filter') is not None:
f = get_metric_filter(root_name, metric.get('filter'))
if f is not None:
metric_filter.append(f)
# in dbt 1.7, there's an additional layer of 'where_filters'
if 'where_filters' in metric.get('filter'):
for where_filter in metric.get('filter').get('where_filters', []):
f = get_metric_filter(root_name, where_filter)
if f is not None:
metric_filter.append(f)
else:
statistics.add_field_one('nosupport')
return None

Check warning on line 479 in piperider_cli/dbtutil.py

View check run for this annotation

Codecov / codecov/patch

piperider_cli/dbtutil.py#L478-L479

Added lines #L478 - L479 were not covered by tests
else:
statistics.add_field_one('nosupport')
return None
f = get_metric_filter(root_name, metric.get('filter'))
if f is not None:
metric_filter.append(f)
else:
statistics.add_field_one('nosupport')
return None

Check warning on line 486 in piperider_cli/dbtutil.py

View check run for this annotation

Codecov / codecov/patch

piperider_cli/dbtutil.py#L485-L486

Added lines #L485 - L486 were not covered by tests

if filter is not None:
f = get_metric_filter(root_name, filter)
if f is not None:
metric_filter.append(f)
if 'where_filters' in filter:
for where_filter in filter.get('where_filters', []):
f = get_metric_filter(root_name, where_filter)
if f is not None:
metric_filter.append(f)
else:
statistics.add_field_one('nosupport')
return None

Check warning on line 496 in piperider_cli/dbtutil.py

View check run for this annotation

Codecov / codecov/patch

piperider_cli/dbtutil.py#L495-L496

Added lines #L495 - L496 were not covered by tests
else:
statistics.add_field_one('nosupport')
return None
f = get_metric_filter(root_name, filter)
if f is not None:
metric_filter.append(f)
else:
statistics.add_field_one('nosupport')
return None

Check warning on line 503 in piperider_cli/dbtutil.py

View check run for this annotation

Codecov / codecov/patch

piperider_cli/dbtutil.py#L502-L503

Added lines #L502 - L503 were not covered by tests

nodes = metric.get('depends_on').get('nodes', [])
depends_on = nodes[0]
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -45,7 +45,7 @@ def _get_version():
'requests>=2.28.1',
'requests_toolbelt>=0.9.1',
'deepmerge',
'dbt-core>=1.3,<1.7'
'dbt-core>=1.3'
],
tests_require=['pytest'],
extras_require={
Expand Down

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions tests/mock_dbt_data/jaffle_shop_base_1_7.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions tests/mock_dbt_data/jaffle_shop_target_1_7.json

Large diffs are not rendered by default.

77 changes: 75 additions & 2 deletions tests/test_dbt_integeration.py
Expand Up @@ -61,6 +61,13 @@ def base_run_1_6(self):
def target_run_1_6(self):
return self.run_object("jaffle_shop_target_1_6.json")

def base_run_1_7(self):
return self.run_object("jaffle_shop_base_1_7.json")

def target_run_1_7(self):
return self.run_object("jaffle_shop_target_1_7.json")


def base_31587(self):
return self.run_object("sc-31587-base.json")

Expand Down Expand Up @@ -187,6 +194,60 @@ def test_list_all_resources_16(self):

self.assertDbtResources(expected, all_results)

@pytest.mark.skipif(dbt_version < 'v1.7', reason='skip manifest test before dbt-core 1.7')
def test_list_all_resources_17(self):
expected = [
"metric.jaffle_shop.average_order_amount",
"metric.jaffle_shop.expenses",
"metric.jaffle_shop.profit",
"metric.jaffle_shop.revenue",
"model.jaffle_shop.int_customer_order_history_joined",
"model.jaffle_shop.int_order_payments_pivoted",
"model.jaffle_shop.metricflow_time_spine",
"model.jaffle_shop.orders",
"model.jaffle_shop.stg_customers",
"model.jaffle_shop.stg_orders",
"model.jaffle_shop.stg_payments",
"seed.jaffle_shop.raw_customers",
"seed.jaffle_shop.raw_orders",
"seed.jaffle_shop.raw_payments",
"semantic_model.jaffle_shop.orders",
"test.jaffle_shop.accepted_values_int_order_payments_pivoted_status__placed__shipped__completed__return_pending__returned.0ccdff53e8",
"test.jaffle_shop.accepted_values_orders_status__placed__shipped__completed__return_pending__returned.be6b5b5ec3",
"test.jaffle_shop.accepted_values_stg_orders_status__placed__shipped__completed__return_pending__returned.080fb20aad",
"test.jaffle_shop.accepted_values_stg_payments_payment_method__credit_card__coupon__bank_transfer__gift_card.3c3820f278",
"test.jaffle_shop.not_null_int_customer_order_history_joined_customer_id.5eeb8cdf92",
"test.jaffle_shop.not_null_int_order_payments_pivoted_amount.b7598e0e3b",
"test.jaffle_shop.not_null_int_order_payments_pivoted_bank_transfer_amount.1a9e62933b",
"test.jaffle_shop.not_null_int_order_payments_pivoted_coupon_amount.2532b538c2",
"test.jaffle_shop.not_null_int_order_payments_pivoted_credit_card_amount.ae9c42d967",
"test.jaffle_shop.not_null_int_order_payments_pivoted_customer_id.3db59c6de4",
"test.jaffle_shop.not_null_int_order_payments_pivoted_gift_card_amount.710d789cc0",
"test.jaffle_shop.not_null_int_order_payments_pivoted_order_id.787ba994a8",
"test.jaffle_shop.not_null_orders_amount.106140f9fd",
"test.jaffle_shop.not_null_orders_bank_transfer_amount.7743500c49",
"test.jaffle_shop.not_null_orders_coupon_amount.ab90c90625",
"test.jaffle_shop.not_null_orders_credit_card_amount.d3ca593b59",
"test.jaffle_shop.not_null_orders_customer_id.c5f02694af",
"test.jaffle_shop.not_null_orders_gift_card_amount.413a0d2d7a",
"test.jaffle_shop.not_null_orders_order_id.cf6c17daed",
"test.jaffle_shop.not_null_stg_customers_customer_id.e2cfb1f9aa",
"test.jaffle_shop.not_null_stg_orders_order_id.81cfe2fe64",
"test.jaffle_shop.not_null_stg_payments_payment_id.c19cc50075",
"test.jaffle_shop.relationships_int_order_payments_pivoted_customer_id__customer_id__ref_int_customer_order_history_joined_.654a1aa35d",
"test.jaffle_shop.unique_int_customer_order_history_joined_customer_id.995635f7d9",
"test.jaffle_shop.unique_int_order_payments_pivoted_order_id.34a0f3307d",
"test.jaffle_shop.unique_orders_order_id.fed79b3a6e",
"test.jaffle_shop.unique_stg_customers_customer_id.c7614daada",
"test.jaffle_shop.unique_stg_orders_order_id.e3b841c71a",
"test.jaffle_shop.unique_stg_payments_payment_id.3744510712"
]

manifest = load_manifest(_load_manifest('dbt-duckdb-1.7.0-manifest.json'))
all_results = list_resources_unique_id_from_manifest(manifest)

self.assertDbtResources(expected, all_results)

def test_compare_with_manifests(self):
without_downstream = compare_models_between_manifests(
self.base_manifest(), self.target_manifest()
Expand All @@ -209,9 +270,15 @@ def test_list_explicit_changes(self):
expected = ["model.jaffle_shop.customers", "model.jaffle_shop.orders"]
changes = c.list_explicit_changes()
self.assertDbtResources(changes, expected)
else:
elif dbt_version < '1.7':
c = GraphDataChangeSet(self.base_run_1_6(), self.target_run_1_6())

expected = ["model.jaffle_shop.orders"]
changes = c.list_explicit_changes()
self.assertDbtResources(changes, expected)
elif dbt_version < '1.8':
c = GraphDataChangeSet(self.base_run_1_7(), self.target_run_1_7())

expected = ["model.jaffle_shop.orders"]
changes = c.list_explicit_changes()
self.assertDbtResources(changes, expected)
Expand Down Expand Up @@ -266,7 +333,7 @@ def test_list_changes_metrics_case(self):
changes,
["metric.jaffle_shop.average_order_amount", "model.jaffle_shop.orders"],
)
else:
elif dbt_version < '1.7':
c = GraphDataChangeSet(self.base_run_1_6(), self.target_run_1_6())
expected = ["model.jaffle_shop.orders"]
changes = c.list_explicit_changes()
Expand All @@ -277,6 +344,12 @@ def test_list_changes_metrics_case(self):
changes,
["metric.jaffle_shop.average_order_amount", "model.jaffle_shop.orders"],
)
elif dbt_version < '1.8':
c = GraphDataChangeSet(self.base_run_1_7(), self.target_run_1_7())

expected = ["model.jaffle_shop.orders"]
changes = c.list_explicit_changes()
self.assertDbtResources(changes, expected)

@unittest.skipIf(
dbt_version < version.parse("1.4"),
Expand Down
11 changes: 11 additions & 0 deletions tests/test_dbt_manifest_compatible.py
Expand Up @@ -34,6 +34,11 @@ def manifest_from_1_6():
return _load_manifest('dbt-duckdb-1.6.0-manifest.json')


@pytest.fixture()
def manifest_from_1_7():
return _load_manifest('dbt-duckdb-1.7.0-manifest.json')


def check_manifest_type(m):
from dbt.contracts.graph.manifest import Manifest, WritableManifest
assert isinstance(m, Manifest) or isinstance(m, WritableManifest)
Expand Down Expand Up @@ -62,6 +67,12 @@ def test_load_manifest_10(manifest_from_1_6):
check_manifest_type(m)


@pytest.mark.skipif(dbt_version < version.parse('v1.7'), reason='skip manifest test before dbt-core 1.7')
def test_load_manifest_11(manifest_from_1_7):
m = load_manifest(manifest_from_1_7)
check_manifest_type(m)


def test_log_functions():
with disable_dbt_compile_stats():
# make sure this not breaking in all dbt versions
Expand Down
28 changes: 27 additions & 1 deletion tests/test_dbt_util.py
Expand Up @@ -252,6 +252,30 @@ def test_get_dbt_state_metrics_16(self, _get_state_manifest):
metrics = dbtutil.get_dbt_state_metrics_16(self.dbt_state_dir, dbt_tag=None, dbt_resources=None)
self.assertEqual(len(metrics), 0)

@pytest.mark.skipif(dbt_version < '1.7', reason="only for dbt 1.7")
@mock.patch('piperider_cli.dbtutil._get_state_manifest')
def test_get_dbt_state_metrics_17(self, _get_state_manifest):
_get_state_manifest.return_value = _load_manifest('dbt-duckdb-1.7.0-manifest.json')
metrics = dbtutil.get_dbt_state_metrics_16(self.dbt_state_dir, dbt_tag=None, dbt_resources=None)

self.assertEqual(len(metrics), 4)

# average_order_amount
self.assertEqual(metrics[0].model.table, 'orders')
self.assertEqual(metrics[0].model.timestamp, 'order_date')
self.assertEqual(metrics[0].model.expression, 'amount')

# profit
self.assertEqual(metrics[1].model, None)
self.assertEqual(metrics[1].calculation_method, 'derived')
self.assertEqual(metrics[1].expression, 'revenue - expenses')

# expenses
self.assertEqual(metrics[2].name, 'expenses')

# revenue
self.assertEqual(metrics[3].calculation_method, 'sum')

@mock.patch('pathlib.Path.cwd',
return_value=Path(os.path.join(os.path.dirname(__file__), 'mock_dbt_project', 'dir_1', 'dir_2')))
def test_search_dbt_project_path(self, *args):
Expand Down Expand Up @@ -284,7 +308,9 @@ def test_is_ready(self):
def test_load_dbt_resources(self, get_dbt_manifest):
v = dbt_version
target_path = os.path.join(os.path.dirname(__file__), 'mock_dbt_data')
if v == '1.6':
if v == '1.7':
get_dbt_manifest.return_value = _load_manifest('dbt-duckdb-1.7.0-manifest.json')
elif v == '1.6':
get_dbt_manifest.return_value = _load_manifest('dbt-duckdb-1.6.0-manifest.json')
elif v == '1.5':
get_dbt_manifest.return_value = _load_manifest('dbt-duckdb-1.5.1-manifest.json')
Expand Down
4 changes: 2 additions & 2 deletions tox-ruamel.ini
Expand Up @@ -10,8 +10,8 @@ wheel_build_env = .pkg

deps =
pytest>=6
dbt-core>=1.6,<1.7
dbt-duckdb>=1.6,<1.7
dbt-core
dbt-duckdb
ruamel-0.17.21: ruamel.yaml<=0.17.21
ruamel-0.17.22: ruamel.yaml>0.17.21,<0.18

Expand Down
4 changes: 2 additions & 2 deletions tox-sqlalchemy.ini
Expand Up @@ -10,8 +10,8 @@ wheel_build_env = .pkg

deps =
pytest>=6
dbt-core>=1.6,<1.7
dbt-duckdb>=1.6,<1.7
dbt-core
dbt-duckdb
sqlalchemy-14: sqlalchemy>=1.4,<2.0
sqlalchemy-20: sqlalchemy>=2.0

Expand Down