Skip to content

Commit

Permalink
Allow tags on exposures (#271)
Browse files Browse the repository at this point in the history
* Allow tags on exposures

* Fix help and tests

* Naming and docs cleanup
  • Loading branch information
gouline authored Jul 25, 2024
1 parent 8647627 commit cc6e8b1
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 26 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ sandbox-exposures:
--metabase-password $$MB_PASSWORD \
--output-path sandbox/models/exposures \
--output-grouping collection \
--tag metabase \
--verbose )

( . sandbox/.env && cd sandbox && \
Expand Down
10 changes: 10 additions & 0 deletions dbtmetabase/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ def _add_setup(func: Callable) -> Callable:
@click.option(
"--http-header",
"http_headers",
metavar="KEY VALUE",
type=(str, str),
multiple=True,
help="Additional HTTP request headers.",
Expand Down Expand Up @@ -377,13 +378,21 @@ def models(
is_flag=True,
help="Exclude items that have not been verified. Only applies to entity types that support verification.",
)
@click.option(
"--tag",
"tags",
metavar="TAG",
multiple=True,
help="Optional tags for exported dbt exposures.",
)
def exposures(
output_path: str,
output_grouping: Optional[str],
include_collections: Optional[Sequence[str]],
exclude_collections: Optional[Sequence[str]],
allow_personal_collections: bool,
exclude_unverified: bool,
tags: Sequence[str],
core: DbtMetabase,
):
core.extract_exposures(
Expand All @@ -395,6 +404,7 @@ def exposures(
),
allow_personal_collections=allow_personal_collections,
exclude_unverified=exclude_unverified,
tags=tags,
)


Expand Down
21 changes: 19 additions & 2 deletions dbtmetabase/_exposures.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@
from abc import ABCMeta, abstractmethod
from operator import itemgetter
from pathlib import Path
from typing import Iterable, Mapping, MutableMapping, MutableSequence, Optional, Tuple
from typing import (
Iterable,
Mapping,
MutableMapping,
MutableSequence,
Optional,
Sequence,
Tuple,
)

from dbtmetabase.metabase import Metabase

Expand Down Expand Up @@ -47,6 +55,7 @@ def extract_exposures(
collection_filter: Optional[Filter] = None,
allow_personal_collections: bool = False,
exclude_unverified: bool = False,
tags: Optional[Sequence[str]] = None,
) -> Iterable[Mapping]:
"""Extract dbt exposures from Metabase.
Expand All @@ -56,6 +65,7 @@ def extract_exposures(
collection_filter (Optional[Filter], optional): Filter Metabase collections. Defaults to None.
allow_personal_collections (bool, optional): Allow personal Metabase collections. Defaults to False.
exclude_unverified (bool, optional): Exclude items that have not been verified. Only applies to entity types that support verification. Defaults to False.
tags (Sequence[str], optional): Optional tags for exported dbt exposures. Defaults to None.
Returns:
Iterable[Mapping]: List of parsed exposures.
Expand Down Expand Up @@ -188,6 +198,7 @@ def extract_exposures(
if depend.lower() in ctx.model_refs
]
),
tags=tags,
),
}
)
Expand Down Expand Up @@ -301,6 +312,7 @@ def __format_exposure(
creator_email: str,
native_query: Optional[str],
depends_on: Iterable[str],
tags: Optional[Sequence[str]],
) -> Mapping:
"""Builds dbt exposure representation (see https://docs.getdbt.com/reference/exposure-properties)."""

Expand Down Expand Up @@ -334,7 +346,7 @@ def __format_exposure(
+ f"Created On: __{created_at}__"
)

return {
exposure = {
"name": name,
"label": label,
"description": safe_description(
Expand All @@ -350,6 +362,11 @@ def __format_exposure(
"depends_on": list(depends_on),
}

if tags:
exposure["tags"] = list(tags)

return exposure

@staticmethod
def __write_exposures(
exposures: Iterable[Mapping],
Expand Down
14 changes: 14 additions & 0 deletions tests/fixtures/exposure/default/exposures.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ exposures:
email: [email protected]
depends_on:
- ref('orders')
tags:
- metabase
- name: dummy
label: Dummy
description: '### Visualization: Table
Expand Down Expand Up @@ -46,6 +48,8 @@ exposures:
name: dbtmetabase
email: [email protected]
depends_on: []
tags:
- metabase
- name: dummy_1
label: Dummy
description: '### Visualization: Table
Expand Down Expand Up @@ -78,6 +82,8 @@ exposures:
name: dbtmetabase
email: [email protected]
depends_on: []
tags:
- metabase
- name: orders___customers
label: Orders + Customers
description: '### Visualization: Table
Expand All @@ -102,6 +108,8 @@ exposures:
depends_on:
- ref('customers')
- ref('orders')
tags:
- metabase
- name: orders___customers__filtered_by_status_is_completed
label: Orders + Customers, Filtered by Status is completed
description: '### Visualization: Table
Expand All @@ -126,6 +134,8 @@ exposures:
depends_on:
- ref('customers')
- ref('orders')
tags:
- metabase
- name: returned_order_count_sql
label: Returned Order Count SQL
description: "### Visualization: Scalar\n\nNo description provided in Metabase\n\
Expand All @@ -141,6 +151,8 @@ exposures:
depends_on:
- ref('stg_orders')
- ref('stg_payments')
tags:
- metabase
- name: the_dashboard
label: The Dashboard
description: '### Dashboard Cards: 3
Expand All @@ -167,3 +179,5 @@ exposures:
- ref('orders')
- ref('stg_orders')
- ref('stg_payments')
tags:
- metabase
52 changes: 28 additions & 24 deletions tests/test_exposures.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

from tests._mocks import FIXTURES_PATH, TMP_PATH, MockDbtMetabase

TMP_PATH.mkdir(exist_ok=True)

def setup_module():
TMP_PATH.mkdir(exist_ok=True)


@pytest.fixture(name="core")
Expand All @@ -23,12 +25,36 @@ def _assert_exposures(expected_path: Path, actual_path: Path):
assert actual["exposures"] == sorted(expected["exposures"], key=itemgetter("name"))


def test_exposures(core: MockDbtMetabase):
def test_exposures_default(core: MockDbtMetabase):
fixtures_path = FIXTURES_PATH / "exposure" / "default"
output_path = TMP_PATH / "exposure" / "default"
core.extract_exposures(
output_path=str(output_path),
output_grouping=None,
tags=["metabase"],
)

_assert_exposures(
fixtures_path / "exposures.yml",
output_path / "exposures.yml",
)


def test_exposures_default_aliased(core: MockDbtMetabase):
for model in core.manifest.read_models():
if not model.name.startswith("stg_"):
model.alias = f"{model.name}_alias"

aliases = [m.alias for m in core.manifest.read_models()]
assert "orders_alias" in aliases
assert "customers_alias" in aliases

fixtures_path = FIXTURES_PATH / "exposure" / "default"
output_path = TMP_PATH / "exposure" / "aliased"
core.extract_exposures(
output_path=str(output_path),
output_grouping=None,
tags=["metabase"],
)

_assert_exposures(
Expand Down Expand Up @@ -62,25 +88,3 @@ def test_exposures_grouping_type(core: MockDbtMetabase):

for file in (fixtures_path / "dashboard").iterdir():
_assert_exposures(file, output_path / "dashboard" / file.name)


def test_exposures_aliased_ref(core: MockDbtMetabase):
for model in core.manifest.read_models():
if not model.name.startswith("stg_"):
model.alias = f"{model.name}_alias"

aliases = [m.alias for m in core.manifest.read_models()]
assert "orders_alias" in aliases
assert "customers_alias" in aliases

fixtures_path = FIXTURES_PATH / "exposure" / "default"
output_path = TMP_PATH / "exposure" / "aliased"
core.extract_exposures(
output_path=str(output_path),
output_grouping=None,
)

_assert_exposures(
fixtures_path / "exposures.yml",
output_path / "exposures.yml",
)

0 comments on commit cc6e8b1

Please sign in to comment.