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

Add config to disable metrics/exposures #5815

Merged
merged 11 commits into from
Sep 14, 2022
Merged
7 changes: 7 additions & 0 deletions .changes/unreleased/Features-20220913-095924.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
kind: Features
body: Add enabled config to exposures and metrics
time: 2022-09-13T09:59:24.445918-05:00
custom:
Author: emmyoop
Issue: "5422"
PR: "5815"
10 changes: 10 additions & 0 deletions core/dbt/config/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,8 @@ def create_project(self, rendered: RenderComponents) -> "Project":
snapshots: Dict[str, Any]
sources: Dict[str, Any]
tests: Dict[str, Any]
metrics: Dict[str, Any]
exposures: Dict[str, Any]
vars_value: VarProvider

dispatch = cfg.dispatch
Expand All @@ -388,6 +390,8 @@ def create_project(self, rendered: RenderComponents) -> "Project":
snapshots = cfg.snapshots
sources = cfg.sources
tests = cfg.tests
metrics = cfg.metrics
exposures = cfg.exposures
if cfg.vars is None:
vars_dict: Dict[str, Any] = {}
else:
Expand Down Expand Up @@ -441,6 +445,8 @@ def create_project(self, rendered: RenderComponents) -> "Project":
query_comment=query_comment,
sources=sources,
tests=tests,
metrics=metrics,
exposures=exposures,
vars=vars_value,
config_version=cfg.config_version,
unrendered=unrendered,
Expand Down Expand Up @@ -543,6 +549,8 @@ class Project:
snapshots: Dict[str, Any]
sources: Dict[str, Any]
tests: Dict[str, Any]
metrics: Dict[str, Any]
exposures: Dict[str, Any]
vars: VarProvider
dbt_version: List[VersionSpecifier]
packages: Dict[str, Any]
Expand Down Expand Up @@ -615,6 +623,8 @@ def to_project_config(self, with_packages=False):
"snapshots": self.snapshots,
"sources": self.sources,
"tests": self.tests,
"metrics": self.metrics,
"exposures": self.exposures,
"vars": self.vars.to_dict(),
"require-dbt-version": [v.to_version_string() for v in self.dbt_version],
"config-version": self.config_version,
Expand Down
8 changes: 8 additions & 0 deletions core/dbt/config/runtime.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ def from_parts(
query_comment=project.query_comment,
sources=project.sources,
tests=project.tests,
metrics=project.metrics,
exposures=project.exposures,
vars=project.vars,
config_version=project.config_version,
unrendered=project.unrendered,
Expand Down Expand Up @@ -274,6 +276,8 @@ def get_resource_config_paths(self) -> Dict[str, PathSet]:
"snapshots": self._get_config_paths(self.snapshots),
"sources": self._get_config_paths(self.sources),
"tests": self._get_config_paths(self.tests),
"metrics": self._get_config_paths(self.metrics),
"exposures": self._get_config_paths(self.exposures),
}

def get_unused_resource_config_paths(
Expand Down Expand Up @@ -477,6 +481,8 @@ def to_project_config(self, with_packages=False):
"snapshots": self.snapshots,
"sources": self.sources,
"tests": self.tests,
"metrics": self.metrics,
"exposures": self.exposures,
"vars": self.vars.to_dict(),
"require-dbt-version": [v.to_version_string() for v in self.dbt_version],
"config-version": self.config_version,
Expand Down Expand Up @@ -537,6 +543,8 @@ def from_parts(
query_comment=project.query_comment,
sources=project.sources,
tests=project.tests,
metrics=project.metrics,
exposures=project.exposures,
vars=project.vars,
config_version=project.config_version,
unrendered=project.unrendered,
Expand Down
9 changes: 8 additions & 1 deletion core/dbt/context/context_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,12 @@ def get_config_dict(self, resource_type: NodeType) -> Dict[str, Any]:
model_configs = unrendered.get("sources")
elif resource_type == NodeType.Test:
model_configs = unrendered.get("tests")
elif resource_type == NodeType.Metric:
model_configs = unrendered.get("metrics")
elif resource_type == NodeType.Exposure:
model_configs = unrendered.get("exposures")
else:
model_configs = unrendered.get("models")

if model_configs is None:
return {}
else:
Expand All @@ -65,6 +68,10 @@ def get_config_dict(self, resource_type: NodeType) -> Dict[str, Any]:
model_configs = self.project.sources
elif resource_type == NodeType.Test:
model_configs = self.project.tests
elif resource_type == NodeType.Metric:
model_configs = self.project.metrics
elif resource_type == NodeType.Exposure:
model_configs = self.project.exposures
else:
model_configs = self.project.models
return model_configs
Expand Down
19 changes: 14 additions & 5 deletions core/dbt/contracts/graph/manifest.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ def perform_lookup(self, unique_id: UniqueID, manifest: "Manifest") -> ParsedMet
return manifest.metrics[unique_id]


# This handles both models/seeds/snapshots and sources
# This handles both models/seeds/snapshots and sources/metrics/exposures
class DisabledLookup(dbtClassMixin):
def __init__(self, manifest: "Manifest"):
self.storage: Dict[str, Dict[PackageName, List[Any]]] = {}
Expand Down Expand Up @@ -464,7 +464,7 @@ class Disabled(Generic[D]):
target: D


MaybeMetricNode = Optional[ParsedMetric]
MaybeMetricNode = Optional[Union[ParsedMetric, Disabled[ParsedMetric]]]


MaybeDocumentation = Optional[ParsedDocumentation]
Expand Down Expand Up @@ -616,7 +616,7 @@ class Manifest(MacroMethods, DataClassMessagePackMixin, dbtClassMixin):
flat_graph: Dict[str, Any] = field(default_factory=dict)
state_check: ManifestStateCheck = field(default_factory=ManifestStateCheck)
source_patches: MutableMapping[SourceKey, SourcePatch] = field(default_factory=dict)
disabled: MutableMapping[str, List[CompileResultNode]] = field(default_factory=dict)
disabled: MutableMapping[str, List[GraphMemberNode]] = field(default_factory=dict)
emmyoop marked this conversation as resolved.
Show resolved Hide resolved
env_vars: MutableMapping[str, str] = field(default_factory=dict)

_doc_lookup: Optional[DocLookup] = field(
Expand Down Expand Up @@ -964,13 +964,22 @@ def resolve_metric(
current_project: str,
node_package: str,
) -> MaybeMetricNode:

metric: Optional[ParsedMetric] = None
disabled: Optional[List[ParsedMetric]] = None

candidates = _search_packages(current_project, node_package, target_metric_package)
for pkg in candidates:
metric = self.metric_lookup.find(target_metric_name, pkg, self)
if metric is not None:

if metric is not None and metric.config.enabled:
return metric

# it's possible that the node is disabled
if disabled is None:
disabled = self.disabled_lookup.find(f"{target_metric_name}", pkg)
if disabled:
return Disabled(disabled[0])
return None

# Called by DocsRuntimeContext.doc
Expand Down Expand Up @@ -1093,7 +1102,7 @@ def add_metric(self, source_file: SchemaSourceFile, metric: ParsedMetric):
self.metrics[metric.unique_id] = metric
source_file.metrics.append(metric.unique_id)

def add_disabled_nofile(self, node: CompileResultNode):
def add_disabled_nofile(self, node: GraphMemberNode):
# There can be multiple disabled nodes for the same unique_id
if node.unique_id in self.disabled:
self.disabled[node.unique_id].append(node)
Expand Down
12 changes: 12 additions & 0 deletions core/dbt/contracts/graph/model_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -363,6 +363,16 @@ def replace(self, **kwargs):
return self.from_dict(dct)


@dataclass
class MetricConfig(BaseConfig):
enabled: bool = True


@dataclass
class ExposureConfig(BaseConfig):
enabled: bool = True


@dataclass
class SourceConfig(BaseConfig):
enabled: bool = True
Expand Down Expand Up @@ -613,6 +623,8 @@ def finalize_and_validate(self):


RESOURCE_TYPES: Dict[NodeType, Type[BaseConfig]] = {
NodeType.Metric: MetricConfig,
NodeType.Exposure: ExposureConfig,
NodeType.Source: SourceConfig,
NodeType.Seed: SeedConfig,
NodeType.Test: TestConfig,
Expand Down
20 changes: 20 additions & 0 deletions core/dbt/contracts/graph/parsed.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@
SeedConfig,
TestConfig,
SourceConfig,
MetricConfig,
ExposureConfig,
EmptySnapshotConfig,
SnapshotConfig,
)
Expand Down Expand Up @@ -746,6 +748,8 @@ class ParsedExposure(UnparsedBaseNode, HasUniqueID, HasFqn):
maturity: Optional[MaturityType] = None
meta: Dict[str, Any] = field(default_factory=dict)
tags: List[str] = field(default_factory=list)
config: ExposureConfig = field(default_factory=ExposureConfig)
unrendered_config: Dict[str, Any] = field(default_factory=dict)
url: Optional[str] = None
depends_on: DependsOn = field(default_factory=DependsOn)
refs: List[List[str]] = field(default_factory=list)
Expand Down Expand Up @@ -778,6 +782,12 @@ def same_exposure_type(self, old: "ParsedExposure") -> bool:
def same_url(self, old: "ParsedExposure") -> bool:
return self.url == old.url

def same_config(self, old: "ParsedExposure") -> bool:
return self.config.same_contents(
self.unrendered_config,
old.unrendered_config,
)

def same_contents(self, old: Optional["ParsedExposure"]) -> bool:
# existing when it didn't before is a change!
# metadata/tags changes are not "changes"
Expand All @@ -792,6 +802,7 @@ def same_contents(self, old: Optional["ParsedExposure"]) -> bool:
and self.same_url(old)
and self.same_description(old)
and self.same_depends_on(old)
and self.same_config(old)
and True
)

Expand Down Expand Up @@ -819,6 +830,8 @@ class ParsedMetric(UnparsedBaseNode, HasUniqueID, HasFqn):
resource_type: NodeType = NodeType.Metric
meta: Dict[str, Any] = field(default_factory=dict)
tags: List[str] = field(default_factory=list)
config: MetricConfig = field(default_factory=MetricConfig)
unrendered_config: Dict[str, Any] = field(default_factory=dict)
sources: List[List[str]] = field(default_factory=list)
depends_on: DependsOn = field(default_factory=DependsOn)
refs: List[List[str]] = field(default_factory=list)
Expand Down Expand Up @@ -863,6 +876,12 @@ def same_timestamp(self, old: "ParsedMetric") -> bool:
def same_time_grains(self, old: "ParsedMetric") -> bool:
return self.time_grains == old.time_grains

def same_config(self, old: "ParsedMetric") -> bool:
return self.config.same_contents(
self.unrendered_config,
old.unrendered_config,
)

def same_contents(self, old: Optional["ParsedMetric"]) -> bool:
# existing when it didn't before is a change!
# metadata/tags changes are not "changes"
Expand All @@ -880,6 +899,7 @@ def same_contents(self, old: Optional["ParsedMetric"]) -> bool:
and self.same_expression(old)
and self.same_timestamp(old)
and self.same_time_grains(old)
and self.same_config(old)
and True
)

Expand Down
7 changes: 2 additions & 5 deletions core/dbt/contracts/graph/unparsed.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,6 +434,7 @@ class UnparsedExposure(dbtClassMixin, Replaceable):
tags: List[str] = field(default_factory=list)
url: Optional[str] = None
depends_on: List[str] = field(default_factory=list)
config: Dict[str, Any] = field(default_factory=dict)


@dataclass
Expand Down Expand Up @@ -465,9 +466,6 @@ def __bool__(self):

@dataclass
class UnparsedMetric(dbtClassMixin, Replaceable):
# TODO : verify that this disallows metric names with spaces
# TODO: fix validation that you broke :p
# name: Identifier
name: str
label: str
calculation_method: str
Expand All @@ -481,11 +479,10 @@ class UnparsedMetric(dbtClassMixin, Replaceable):
filters: List[MetricFilter] = field(default_factory=list)
meta: Dict[str, Any] = field(default_factory=dict)
tags: List[str] = field(default_factory=list)
config: Dict[str, Any] = field(default_factory=dict)

@classmethod
def validate(cls, data):
# super().validate(data)
# TODO: putting this back for now to get tests passing. Do we want to implement name: Identifier?
super(UnparsedMetric, cls).validate(data)
if "name" in data and " " in data["name"]:
raise ParsingException(f"Metrics name '{data['name']}' cannot contain spaces")
Expand Down
2 changes: 2 additions & 0 deletions core/dbt/contracts/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,8 @@ class Project(HyphenatedDbtClassMixin, Replaceable):
analyses: Dict[str, Any] = field(default_factory=dict)
sources: Dict[str, Any] = field(default_factory=dict)
tests: Dict[str, Any] = field(default_factory=dict)
metrics: Dict[str, Any] = field(default_factory=dict)
exposures: Dict[str, Any] = field(default_factory=dict)
vars: Optional[Dict[str, Any]] = field(
default=None,
metadata=dict(
Expand Down
57 changes: 40 additions & 17 deletions core/dbt/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -631,46 +631,69 @@ def ref_target_not_found(
raise_compiler_error(msg, model)


def get_source_not_found_or_disabled_msg(
model,
def get_not_found_or_disabled_msg(
node,
target_name: str,
target_table_name: str,
target_kind: str,
target_package: Optional[str] = None,
disabled: Optional[bool] = None,
) -> str:
full_name = f"{target_name}.{target_table_name}"
if disabled is None:
reason = "was not found or is disabled"
elif disabled is True:
reason = "is disabled"
else:
reason = "was not found"
return _get_target_failure_msg(
model, full_name, None, include_path=True, reason=reason, target_kind="source"
node,
target_name,
target_package,
include_path=True,
reason=reason,
target_kind=target_kind,
)


def source_target_not_found(
model, target_name: str, target_table_name: str, disabled: Optional[bool] = None
) -> NoReturn:
msg = get_source_not_found_or_disabled_msg(model, target_name, target_table_name, disabled)
msg = get_not_found_or_disabled_msg(
node=model,
target_name=f"{target_name}.{target_table_name}",
target_kind="source",
disabled=disabled,
)
raise_compiler_error(msg, model)


def get_metric_not_found_msg(
model,
target_name: str,
target_package: Optional[str],
) -> str:
reason = "was not found"
return _get_target_failure_msg(
model, target_name, target_package, include_path=True, reason=reason, target_kind="metric"
def metric_target_not_found(
metric, target_name: str, target_package: Optional[str], disabled: Optional[bool] = None
) -> NoReturn:

msg = get_not_found_or_disabled_msg(
node=metric,
target_name=target_name,
target_kind="metric",
target_package=target_package,
disabled=disabled,
)

raise_compiler_error(msg, metric)

def metric_target_not_found(metric, target_name: str, target_package: Optional[str]) -> NoReturn:
msg = get_metric_not_found_msg(metric, target_name, target_package)

raise_compiler_error(msg, metric)
def exposure_target_not_found(
exposure, target_name: str, target_package: Optional[str], disabled: Optional[bool] = None
) -> NoReturn:

msg = get_not_found_or_disabled_msg(
node=exposure,
target_name=target_name,
target_kind="exposure",
target_package=target_package,
disabled=disabled,
)

raise_compiler_error(msg, exposure)


def dependency_not_found(model, target_model_name):
Expand Down
Loading