diff --git a/CHANGELOG.md b/CHANGELOG.md index 30c6fa4b409..ab5bdee6a55 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,7 @@ ### Features - Specify all three logging levels (`INFO`, `WARNING`, `ERROR`) in result logs for commands `test`, `seed`, `run`, `snapshot` and `source snapshot-freshness` ([#2680](https://github.com/fishtown-analytics/dbt/pull/2680), [#2723](https://github.com/fishtown-analytics/dbt/pull/2723)) -- Added "reports" ([#2730](https://github.com/fishtown-analytics/dbt/issues/2730), [#2752](https://github.com/fishtown-analytics/dbt/pull/2752)) +- Added "exposures" ([#2730](https://github.com/fishtown-analytics/dbt/issues/2730), [#2752](https://github.com/fishtown-analytics/dbt/pull/2752), [#2777](https://github.com/fishtown-analytics/dbt/issues/2777)) ### Docs - Add Report nodes ([docs#135](https://github.com/fishtown-analytics/dbt-docs/issues/135), [docs#136](https://github.com/fishtown-analytics/dbt-docs/pull/136)) diff --git a/core/dbt/compilation.py b/core/dbt/compilation.py index 5811227efe4..a2320d45710 100644 --- a/core/dbt/compilation.py +++ b/core/dbt/compilation.py @@ -426,9 +426,9 @@ def link_graph(self, linker: Linker, manifest: Manifest): linker.add_node(source.unique_id) for node in manifest.nodes.values(): self.link_node(linker, node, manifest) - for report in manifest.reports.values(): - self.link_node(linker, report, manifest) - # linker.add_node(report.unique_id) + for exposure in manifest.exposures.values(): + self.link_node(linker, exposure, manifest) + # linker.add_node(exposure.unique_id) cycle = linker.find_cycles() diff --git a/core/dbt/context/providers.py b/core/dbt/context/providers.py index e2d9fca2035..d23fa623806 100644 --- a/core/dbt/context/providers.py +++ b/core/dbt/context/providers.py @@ -25,7 +25,7 @@ ) from dbt.contracts.graph.parsed import ( ParsedMacro, - ParsedReport, + ParsedExposure, ParsedSeedNode, ParsedSourceDefinition, ) @@ -1325,7 +1325,7 @@ def generate_runtime_macro( return ctx.to_dict() -class ReportRefResolver(BaseResolver): +class ExposureRefResolver(BaseResolver): def __call__(self, *args) -> str: if len(args) not in (1, 2): ref_invalid_args(self.model, args) @@ -1333,7 +1333,7 @@ def __call__(self, *args) -> str: return '' -class ReportSourceResolver(BaseResolver): +class ExposureSourceResolver(BaseResolver): def __call__(self, *args) -> str: if len(args) != 2: raise_compiler_error( @@ -1344,23 +1344,23 @@ def __call__(self, *args) -> str: return '' -def generate_parse_report( - report: ParsedReport, +def generate_parse_exposure( + exposure: ParsedExposure, config: RuntimeConfig, manifest: Manifest, package_name: str, ) -> Dict[str, Any]: project = config.load_dependencies()[package_name] return { - 'ref': ReportRefResolver( + 'ref': ExposureRefResolver( None, - report, + exposure, project, manifest, ), - 'source': ReportSourceResolver( + 'source': ExposureSourceResolver( None, - report, + exposure, project, manifest, ) diff --git a/core/dbt/contracts/files.py b/core/dbt/contracts/files.py index 4ef0bab54cc..0ab18f2e525 100644 --- a/core/dbt/contracts/files.py +++ b/core/dbt/contracts/files.py @@ -121,7 +121,7 @@ class SourceFile(JsonSchemaMixin): docs: List[str] = field(default_factory=list) macros: List[str] = field(default_factory=list) sources: List[str] = field(default_factory=list) - reports: List[str] = field(default_factory=list) + exposures: List[str] = field(default_factory=list) # any node patches in this file. The entries are names, not unique ids! patches: List[str] = field(default_factory=list) # any macro patches in this file. The entries are package, name pairs. diff --git a/core/dbt/contracts/graph/compiled.py b/core/dbt/contracts/graph/compiled.py index 2eeae63861b..66250ba1821 100644 --- a/core/dbt/contracts/graph/compiled.py +++ b/core/dbt/contracts/graph/compiled.py @@ -5,7 +5,7 @@ ParsedDataTestNode, ParsedHookNode, ParsedModelNode, - ParsedReport, + ParsedExposure, ParsedResource, ParsedRPCNode, ParsedSchemaTestNode, @@ -216,8 +216,8 @@ def parsed_instance_for(compiled: CompiledNode) -> ParsedResource: ParsedSourceDefinition, ] -# anything that participates in the graph: sources, reports, manifest nodes +# anything that participates in the graph: sources, exposures, manifest nodes GraphMemberNode = Union[ CompileResultNode, - ParsedReport, + ParsedExposure, ] diff --git a/core/dbt/contracts/graph/manifest.py b/core/dbt/contracts/graph/manifest.py index f2f5f476a44..eeaf14eb27c 100644 --- a/core/dbt/contracts/graph/manifest.py +++ b/core/dbt/contracts/graph/manifest.py @@ -18,7 +18,7 @@ ) from dbt.contracts.graph.parsed import ( ParsedMacro, ParsedDocumentation, ParsedNodePatch, ParsedMacroPatch, - ParsedSourceDefinition, ParsedReport + ParsedSourceDefinition, ParsedExposure ) from dbt.contracts.files import SourceFile from dbt.contracts.util import ( @@ -429,7 +429,7 @@ class Manifest: sources: MutableMapping[str, ParsedSourceDefinition] macros: MutableMapping[str, ParsedMacro] docs: MutableMapping[str, ParsedDocumentation] - reports: MutableMapping[str, ParsedReport] + exposures: MutableMapping[str, ParsedExposure] generated_at: datetime disabled: List[CompileResultNode] files: MutableMapping[str, SourceFile] @@ -455,7 +455,7 @@ def from_macros( sources={}, macros=macros, docs={}, - reports={}, + exposures={}, generated_at=datetime.utcnow(), disabled=[], files=files, @@ -481,8 +481,8 @@ def sync_update_node( _update_into(self.nodes, new_node) return new_node - def update_report(self, new_report: ParsedReport): - _update_into(self.reports, new_report) + def update_exposure(self, new_exposure: ParsedExposure): + _update_into(self.exposures, new_exposure) def update_node(self, new_node: ManifestNode): _update_into(self.nodes, new_node) @@ -725,7 +725,7 @@ def deepcopy(self): sources={k: _deepcopy(v) for k, v in self.sources.items()}, macros={k: _deepcopy(v) for k, v in self.macros.items()}, docs={k: _deepcopy(v) for k, v in self.docs.items()}, - reports={k: _deepcopy(v) for k, v in self.reports.items()}, + exposures={k: _deepcopy(v) for k, v in self.exposures.items()}, generated_at=self.generated_at, disabled=[_deepcopy(n) for n in self.disabled], metadata=self.metadata, @@ -736,7 +736,7 @@ def writable_manifest(self): edge_members = list(chain( self.nodes.values(), self.sources.values(), - self.reports.values(), + self.exposures.values(), )) forward_edges, backward_edges = build_edges(edge_members) @@ -745,7 +745,7 @@ def writable_manifest(self): sources=self.sources, macros=self.macros, docs=self.docs, - reports=self.reports, + exposures=self.exposures, generated_at=self.generated_at, metadata=self.metadata, disabled=self.disabled, @@ -766,8 +766,8 @@ def expect(self, unique_id: str) -> GraphMemberNode: return self.nodes[unique_id] elif unique_id in self.sources: return self.sources[unique_id] - elif unique_id in self.reports: - return self.reports[unique_id] + elif unique_id in self.exposures: + return self.exposures[unique_id] else: # something terrible has happened raise dbt.exceptions.InternalException( @@ -910,7 +910,7 @@ def __reduce_ex__(self, protocol): self.sources, self.macros, self.docs, - self.reports, + self.exposures, self.generated_at, self.disabled, self.files, @@ -945,9 +945,9 @@ class WritableManifest(JsonSchemaMixin, Writable, Readable): 'The docs defined in the dbt project and its dependencies' )) ) - reports: Mapping[UniqueID, ParsedReport] = field( + exposures: Mapping[UniqueID, ParsedExposure] = field( metadata=dict(description=( - 'The reports defined in the dbt project and its dependencies' + 'The exposures defined in the dbt project and its dependencies' )) ) disabled: Optional[List[CompileResultNode]] = field(metadata=dict( diff --git a/core/dbt/contracts/graph/parsed.py b/core/dbt/contracts/graph/parsed.py index dccf90d53cf..a7b2f175304 100644 --- a/core/dbt/contracts/graph/parsed.py +++ b/core/dbt/contracts/graph/parsed.py @@ -23,7 +23,7 @@ UnparsedBaseNode, FreshnessThreshold, ExternalTable, HasYamlMetadata, MacroArgument, UnparsedSourceDefinition, UnparsedSourceTableDefinition, UnparsedColumn, TestDef, - ReportOwner, ExposureType, MaturityType + ExposureOwner, ExposureType, MaturityType ) from dbt.contracts.util import Replaceable, AdditionalPropertiesMixin from dbt.exceptions import warn_or_error @@ -636,11 +636,11 @@ def search_name(self): @dataclass -class ParsedReport(UnparsedBaseNode, HasUniqueID, HasFqn): +class ParsedExposure(UnparsedBaseNode, HasUniqueID, HasFqn): name: str type: ExposureType - owner: ReportOwner - resource_type: NodeType = NodeType.Report + owner: ExposureOwner + resource_type: NodeType = NodeType.Exposure maturity: Optional[MaturityType] = None url: Optional[str] = None description: Optional[str] = None @@ -661,25 +661,25 @@ def search_name(self): def tags(self): return [] - def same_depends_on(self, old: 'ParsedReport') -> bool: + def same_depends_on(self, old: 'ParsedExposure') -> bool: return set(self.depends_on.nodes) == set(old.depends_on.nodes) - def same_description(self, old: 'ParsedReport') -> bool: + def same_description(self, old: 'ParsedExposure') -> bool: return self.description == old.description - def same_maturity(self, old: 'ParsedReport') -> bool: + def same_maturity(self, old: 'ParsedExposure') -> bool: return self.maturity == old.maturity - def same_owner(self, old: 'ParsedReport') -> bool: + def same_owner(self, old: 'ParsedExposure') -> bool: return self.owner == old.owner - def same_exposure_type(self, old: 'ParsedReport') -> bool: + def same_exposure_type(self, old: 'ParsedExposure') -> bool: return self.type == old.type - def same_url(self, old: 'ParsedReport') -> bool: + def same_url(self, old: 'ParsedExposure') -> bool: return self.url == old.url - def same_contents(self, old: Optional['ParsedReport']) -> bool: + def same_contents(self, old: Optional['ParsedExposure']) -> bool: # existing when it didn't before is a change! if old is None: return True @@ -700,6 +700,6 @@ def same_contents(self, old: Optional['ParsedReport']) -> bool: ParsedDocumentation, ParsedMacro, ParsedNode, - ParsedReport, + ParsedExposure, ParsedSourceDefinition, ] diff --git a/core/dbt/contracts/graph/unparsed.py b/core/dbt/contracts/graph/unparsed.py index b14f5fe2202..57da34f4a24 100644 --- a/core/dbt/contracts/graph/unparsed.py +++ b/core/dbt/contracts/graph/unparsed.py @@ -405,16 +405,16 @@ class MaturityType(StrEnum): @dataclass -class ReportOwner(JsonSchemaMixin, Replaceable): +class ExposureOwner(JsonSchemaMixin, Replaceable): email: str name: Optional[str] = None @dataclass -class UnparsedReport(JsonSchemaMixin, Replaceable): +class UnparsedExposure(JsonSchemaMixin, Replaceable): name: str type: ExposureType - owner: ReportOwner + owner: ExposureOwner maturity: Optional[MaturityType] = None url: Optional[str] = None description: Optional[str] = None diff --git a/core/dbt/graph/cli.py b/core/dbt/graph/cli.py index b743022472c..10502fd16de 100644 --- a/core/dbt/graph/cli.py +++ b/core/dbt/graph/cli.py @@ -18,7 +18,7 @@ INTERSECTION_DELIMITER = ',' -DEFAULT_INCLUDES: List[str] = ['fqn:*', 'source:*', 'report:*'] +DEFAULT_INCLUDES: List[str] = ['fqn:*', 'source:*', 'exposure:*'] DEFAULT_EXCLUDES: List[str] = [] DATA_TEST_SELECTOR: str = 'test_type:data' SCHEMA_TEST_SELECTOR: str = 'test_type:schema' diff --git a/core/dbt/graph/queue.py b/core/dbt/graph/queue.py index f08a6ad97a5..ee01504fef1 100644 --- a/core/dbt/graph/queue.py +++ b/core/dbt/graph/queue.py @@ -7,7 +7,7 @@ import networkx as nx # type: ignore from .graph import UniqueId -from dbt.contracts.graph.parsed import ParsedSourceDefinition, ParsedReport +from dbt.contracts.graph.parsed import ParsedSourceDefinition, ParsedExposure from dbt.contracts.graph.compiled import GraphMemberNode from dbt.contracts.graph.manifest import Manifest from dbt.node_types import NodeType @@ -50,8 +50,8 @@ def _include_in_cost(self, node_id: UniqueId) -> bool: node = self.manifest.expect(node_id) if node.resource_type != NodeType.Model: return False - # must be a Model - tell mypy this won't be a Source or Report - assert not isinstance(node, (ParsedSourceDefinition, ParsedReport)) + # must be a Model - tell mypy this won't be a Source or Exposure + assert not isinstance(node, (ParsedSourceDefinition, ParsedExposure)) if node.is_ephemeral: return False return True diff --git a/core/dbt/graph/selector.py b/core/dbt/graph/selector.py index ec687c0ca24..5376c74cb0f 100644 --- a/core/dbt/graph/selector.py +++ b/core/dbt/graph/selector.py @@ -129,7 +129,7 @@ def _is_graph_member(self, unique_id: UniqueId) -> bool: if unique_id in self.manifest.sources: source = self.manifest.sources[unique_id] return source.config.enabled - elif unique_id in self.manifest.reports: + elif unique_id in self.manifest.exposures: return True node = self.manifest.nodes[unique_id] return not node.empty and node.config.enabled @@ -146,8 +146,8 @@ def _is_match(self, unique_id: UniqueId) -> bool: node = self.manifest.nodes[unique_id] elif unique_id in self.manifest.sources: node = self.manifest.sources[unique_id] - elif unique_id in self.manifest.reports: - node = self.manifest.reports[unique_id] + elif unique_id in self.manifest.exposures: + node = self.manifest.exposures[unique_id] else: raise InternalException( f'Node {unique_id} not found in the manifest!' diff --git a/core/dbt/graph/selector_methods.py b/core/dbt/graph/selector_methods.py index 5ecac01c51a..b43efce45ce 100644 --- a/core/dbt/graph/selector_methods.py +++ b/core/dbt/graph/selector_methods.py @@ -17,7 +17,7 @@ from dbt.contracts.graph.parsed import ( HasTestMetadata, ParsedDataTestNode, - ParsedReport, + ParsedExposure, ParsedSchemaTestNode, ParsedSourceDefinition, ) @@ -46,7 +46,7 @@ class MethodName(StrEnum): TestType = 'test_type' ResourceType = 'resource_type' State = 'state' - Report = 'report' + Exposure = 'exposure' def is_selected_node(real_node, node_selector): @@ -75,7 +75,7 @@ def is_selected_node(real_node, node_selector): return True -SelectorTarget = Union[ParsedSourceDefinition, ManifestNode, ParsedReport] +SelectorTarget = Union[ParsedSourceDefinition, ManifestNode, ParsedExposure] class SelectorMethod(metaclass=abc.ABCMeta): @@ -111,16 +111,16 @@ def source_nodes( continue yield unique_id, source - def report_nodes( + def exposure_nodes( self, included_nodes: Set[UniqueId] - ) -> Iterator[Tuple[UniqueId, ParsedReport]]: + ) -> Iterator[Tuple[UniqueId, ParsedExposure]]: - for key, report in self.manifest.reports.items(): + for key, exposure in self.manifest.exposures.items(): unique_id = UniqueId(key) if unique_id not in included_nodes: continue - yield unique_id, report + yield unique_id, exposure def all_nodes( self, @@ -128,7 +128,7 @@ def all_nodes( ) -> Iterator[Tuple[UniqueId, SelectorTarget]]: yield from chain(self.parsed_nodes(included_nodes), self.source_nodes(included_nodes), - self.report_nodes(included_nodes)) + self.exposure_nodes(included_nodes)) def configurable_nodes( self, @@ -140,9 +140,9 @@ def configurable_nodes( def non_source_nodes( self, included_nodes: Set[UniqueId], - ) -> Iterator[Tuple[UniqueId, Union[ParsedReport, ManifestNode]]]: + ) -> Iterator[Tuple[UniqueId, Union[ParsedExposure, ManifestNode]]]: yield from chain(self.parsed_nodes(included_nodes), - self.report_nodes(included_nodes)) + self.exposure_nodes(included_nodes)) @abc.abstractmethod def search( @@ -244,7 +244,7 @@ def search( yield node -class ReportSelectorMethod(SelectorMethod): +class ExposureSelectorMethod(SelectorMethod): def search( self, included_nodes: Set[UniqueId], selector: str ) -> Iterator[UniqueId]: @@ -256,13 +256,13 @@ def search( target_package, target_name = parts else: msg = ( - 'Invalid report selector value "{}". Reports must be of ' - 'the form ${{report_name}} or ' - '${{report_package.report_name}}' + 'Invalid exposure selector value "{}". Exposures must be of ' + 'the form ${{exposure_name}} or ' + '${{exposure_package.exposure_name}}' ).format(selector) raise RuntimeException(msg) - for node, real_node in self.report_nodes(included_nodes): + for node, real_node in self.exposure_nodes(included_nodes): if target_package not in (real_node.package_name, SELECTOR_GLOB): continue if target_name not in (real_node.name, SELECTOR_GLOB): @@ -481,8 +481,8 @@ def search( previous_node = manifest.nodes[node] elif node in manifest.sources: previous_node = manifest.sources[node] - elif node in manifest.reports: - previous_node = manifest.reports[node] + elif node in manifest.exposures: + previous_node = manifest.exposures[node] if checker(previous_node, real_node): yield node @@ -499,7 +499,7 @@ class MethodManager: MethodName.TestName: TestNameSelectorMethod, MethodName.TestType: TestTypeSelectorMethod, MethodName.State: StateSelectorMethod, - MethodName.Report: ReportSelectorMethod, + MethodName.Exposure: ExposureSelectorMethod, } def __init__( diff --git a/core/dbt/node_types.py b/core/dbt/node_types.py index 1d14be2032f..8ba6d5900ea 100644 --- a/core/dbt/node_types.py +++ b/core/dbt/node_types.py @@ -14,7 +14,7 @@ class NodeType(StrEnum): Documentation = 'docs' Source = 'source' Macro = 'macro' - Report = 'report' + Exposure = 'exposure' @classmethod def executable(cls) -> List['NodeType']: diff --git a/core/dbt/parser/manifest.py b/core/dbt/parser/manifest.py index 3b353f6a5a9..65a7235b8ca 100644 --- a/core/dbt/parser/manifest.py +++ b/core/dbt/parser/manifest.py @@ -23,7 +23,7 @@ from dbt.contracts.graph.compiled import ManifestNode from dbt.contracts.graph.manifest import Manifest, Disabled from dbt.contracts.graph.parsed import ( - ParsedSourceDefinition, ParsedNode, ParsedMacro, ColumnInfo, ParsedReport + ParsedSourceDefinition, ParsedNode, ParsedMacro, ColumnInfo, ParsedExposure ) from dbt.exceptions import ( ref_target_not_found, @@ -316,7 +316,7 @@ def create_manifest(self) -> Manifest: sources=sources, macros=self.results.macros, docs=self.results.docs, - reports=self.results.reports, + exposures=self.results.exposures, generated_at=datetime.utcnow(), metadata=self.root_project.get_metadata(), disabled=disabled, @@ -553,11 +553,11 @@ def process_docs(manifest: Manifest, config: RuntimeConfig): _process_docs_for_macro(ctx, macro) -def _process_refs_for_report( - manifest: Manifest, current_project: str, report: ParsedReport +def _process_refs_for_exposure( + manifest: Manifest, current_project: str, exposure: ParsedExposure ): - """Given a manifest and a report in that manifest, process its refs""" - for ref in report.refs: + """Given a manifest and a exposure in that manifest, process its refs""" + for ref in exposure.refs: target_model: Optional[Union[Disabled, ManifestNode]] = None target_model_name: str target_model_package: Optional[str] = None @@ -575,14 +575,14 @@ def _process_refs_for_report( target_model_name, target_model_package, current_project, - report.package_name, + exposure.package_name, ) if target_model is None or isinstance(target_model, Disabled): # This may raise. Even if it doesn't, we don't want to add - # this report to the graph b/c there is no destination report + # this exposure to the graph b/c there is no destination exposure invalid_ref_fail_unless_test( - report, target_model_name, target_model_package, + exposure, target_model_name, target_model_package, disabled=(isinstance(target_model, Disabled)) ) @@ -590,8 +590,8 @@ def _process_refs_for_report( target_model_id = target_model.unique_id - report.depends_on.nodes.append(target_model_id) - manifest.update_report(report) + exposure.depends_on.nodes.append(target_model_id) + manifest.update_exposure(exposure) def _process_refs_for_node( @@ -642,33 +642,33 @@ def _process_refs_for_node( def process_refs(manifest: Manifest, current_project: str): for node in manifest.nodes.values(): _process_refs_for_node(manifest, current_project, node) - for report in manifest.reports.values(): - _process_refs_for_report(manifest, current_project, report) + for exposure in manifest.exposures.values(): + _process_refs_for_exposure(manifest, current_project, exposure) return manifest -def _process_sources_for_report( - manifest: Manifest, current_project: str, report: ParsedReport +def _process_sources_for_exposure( + manifest: Manifest, current_project: str, exposure: ParsedExposure ): target_source: Optional[Union[Disabled, ParsedSourceDefinition]] = None - for source_name, table_name in report.sources: + for source_name, table_name in exposure.sources: target_source = manifest.resolve_source( source_name, table_name, current_project, - report.package_name, + exposure.package_name, ) if target_source is None or isinstance(target_source, Disabled): invalid_source_fail_unless_test( - report, + exposure, source_name, table_name, disabled=(isinstance(target_source, Disabled)) ) continue target_source_id = target_source.unique_id - report.depends_on.nodes.append(target_source_id) - manifest.update_report(report) + exposure.depends_on.nodes.append(target_source_id) + manifest.update_exposure(exposure) def _process_sources_for_node( @@ -704,8 +704,8 @@ def process_sources(manifest: Manifest, current_project: str): continue assert not isinstance(node, ParsedSourceDefinition) _process_sources_for_node(manifest, current_project, node) - for report in manifest.reports.values(): - _process_sources_for_report(manifest, current_project, report) + for exposure in manifest.exposures.values(): + _process_sources_for_exposure(manifest, current_project, exposure) return manifest diff --git a/core/dbt/parser/results.py b/core/dbt/parser/results.py index 44ff65f9798..a1b5def9df2 100644 --- a/core/dbt/parser/results.py +++ b/core/dbt/parser/results.py @@ -15,7 +15,7 @@ ParsedMacroPatch, ParsedModelNode, ParsedNodePatch, - ParsedReport, + ParsedExposure, ParsedRPCNode, ParsedSeedNode, ParsedSchemaTestNode, @@ -70,7 +70,7 @@ class ParseResult(JsonSchemaMixin, Writable, Replaceable): sources: MutableMapping[str, UnpatchedSourceDefinition] = dict_field() docs: MutableMapping[str, ParsedDocumentation] = dict_field() macros: MutableMapping[str, ParsedMacro] = dict_field() - reports: MutableMapping[str, ParsedReport] = dict_field() + exposures: MutableMapping[str, ParsedExposure] = dict_field() macro_patches: MutableMapping[MacroKey, ParsedMacroPatch] = dict_field() patches: MutableMapping[str, ParsedNodePatch] = dict_field() source_patches: MutableMapping[SourceKey, SourcePatch] = dict_field() @@ -103,10 +103,10 @@ def add_node(self, source_file: SourceFile, node: ManifestNodes): self.add_node_nofile(node) self.get_file(source_file).nodes.append(node.unique_id) - def add_report(self, source_file: SourceFile, report: ParsedReport): - _check_duplicates(report, self.reports) - self.reports[report.unique_id] = report - self.get_file(source_file).reports.append(report.unique_id) + def add_exposure(self, source_file: SourceFile, exposure: ParsedExposure): + _check_duplicates(exposure, self.exposures) + self.exposures[exposure.unique_id] = exposure + self.get_file(source_file).exposures.append(exposure.unique_id) def add_disabled_nofile(self, node: CompileResultNode): if node.unique_id in self.disabled: @@ -269,11 +269,11 @@ def sanitized_update( continue self._process_node(node_id, source_file, old_file, old_result) - for report_id in old_file.reports: - report = _expect_value( - report_id, old_result.reports, old_file, "reports" + for exposure_id in old_file.exposures: + exposure = _expect_value( + exposure_id, old_result.exposures, old_file, "exposures" ) - self.add_report(source_file, report) + self.add_exposure(source_file, exposure) patched = False for name in old_file.patches: diff --git a/core/dbt/parser/schema_test_builders.py b/core/dbt/parser/schema_test_builders.py index 81668845b57..2832aaeeeef 100644 --- a/core/dbt/parser/schema_test_builders.py +++ b/core/dbt/parser/schema_test_builders.py @@ -13,7 +13,7 @@ UnparsedAnalysisUpdate, UnparsedMacroUpdate, UnparsedNodeUpdate, - UnparsedReport, + UnparsedExposure, ) from dbt.exceptions import raise_compiler_error from dbt.parser.search import FileBlock @@ -82,7 +82,7 @@ def from_file_block(cls, src: FileBlock, data: Dict[str, Any]): UnparsedMacroUpdate, UnparsedAnalysisUpdate, UnpatchedSourceDefinition, - UnparsedReport, + UnparsedExposure, ) diff --git a/core/dbt/parser/schemas.py b/core/dbt/parser/schemas.py index 1603d63f5a7..c7a3fab83fb 100644 --- a/core/dbt/parser/schemas.py +++ b/core/dbt/parser/schemas.py @@ -18,7 +18,7 @@ ) from dbt.context.configured import generate_schema_yml from dbt.context.target import generate_target_context -from dbt.context.providers import generate_parse_report +from dbt.context.providers import generate_parse_exposure from dbt.contracts.files import FileHash from dbt.contracts.graph.manifest import SourceFile from dbt.contracts.graph.model_config import SourceConfig @@ -29,7 +29,7 @@ ParsedSchemaTestNode, ParsedMacroPatch, UnpatchedSourceDefinition, - ParsedReport, + ParsedExposure, ) from dbt.contracts.graph.unparsed import ( FreshnessThreshold, @@ -41,7 +41,7 @@ UnparsedColumn, UnparsedMacroUpdate, UnparsedNodeUpdate, - UnparsedReport, + UnparsedExposure, UnparsedSourceDefinition, ) from dbt.exceptions import ( @@ -521,10 +521,10 @@ def parse_tests(self, block: TestBlock) -> None: for test in block.tests: self.parse_test(block, test, None) - def parse_reports(self, block: YamlBlock) -> None: - parser = ReportParser(self, block) + def parse_exposures(self, block: YamlBlock) -> None: + parser = ExposureParser(self, block) for node in parser.parse(): - self.results.add_report(block.file, node) + self.results.add_exposure(block.file, node) def parse_file(self, block: FileBlock) -> None: dct = self._yaml_from_file(block.file) @@ -556,7 +556,7 @@ def parse_file(self, block: FileBlock) -> None: parser = TestablePatchParser(self, yaml_block, plural) for test_block in parser.parse(): self.parse_tests(test_block) - self.parse_reports(yaml_block) + self.parse_exposures(yaml_block) Parsed = TypeVar( @@ -783,21 +783,21 @@ def parse_patch( self.results.add_macro_patch(self.yaml.file, result) -class ReportParser(YamlReader): +class ExposureParser(YamlReader): def __init__(self, schema_parser: SchemaParser, yaml: YamlBlock): - super().__init__(schema_parser, yaml, NodeType.Report.pluralize()) + super().__init__(schema_parser, yaml, NodeType.Exposure.pluralize()) self.schema_parser = schema_parser self.yaml = yaml - def parse_report(self, unparsed: UnparsedReport) -> ParsedReport: + def parse_exposure(self, unparsed: UnparsedExposure) -> ParsedExposure: package_name = self.project.project_name - unique_id = f'{NodeType.Report}.{package_name}.{unparsed.name}' + unique_id = f'{NodeType.Exposure}.{package_name}.{unparsed.name}' path = self.yaml.path.relative_path fqn = self.schema_parser.get_fqn_prefix(path) fqn.append(unparsed.name) - parsed = ParsedReport( + parsed = ParsedExposure( package_name=package_name, root_path=self.project.project_root, path=path, @@ -811,7 +811,7 @@ def parse_report(self, unparsed: UnparsedReport) -> ParsedReport: owner=unparsed.owner, maturity=unparsed.maturity, ) - ctx = generate_parse_report( + ctx = generate_parse_exposure( parsed, self.root_project, self.schema_parser.macro_manifest, @@ -826,12 +826,12 @@ def parse_report(self, unparsed: UnparsedReport) -> ParsedReport: # parsed now has a populated refs/sources return parsed - def parse(self) -> Iterable[ParsedReport]: + def parse(self) -> Iterable[ParsedExposure]: for data in self.get_key_dicts(): try: - unparsed = UnparsedReport.from_dict(data) + unparsed = UnparsedExposure.from_dict(data) except (ValidationError, JSONValidationException) as exc: msg = error_context(self.yaml.path, self.key, data, exc) raise CompilationException(msg) from exc - parsed = self.parse_report(unparsed) + parsed = self.parse_exposure(unparsed) yield parsed diff --git a/core/dbt/task/list.py b/core/dbt/task/list.py index 9e563fd0223..8d25ba612b0 100644 --- a/core/dbt/task/list.py +++ b/core/dbt/task/list.py @@ -2,7 +2,7 @@ from typing import Type from dbt.contracts.graph.parsed import ( - ParsedReport, + ParsedExposure, ParsedSourceDefinition, ) from dbt.graph import ( @@ -24,7 +24,7 @@ class ListTask(GraphRunnableTask): NodeType.Seed, NodeType.Test, NodeType.Source, - NodeType.Report, + NodeType.Exposure, )) ALL_RESOURCE_VALUES = DEFAULT_RESOURCE_VALUES | frozenset(( NodeType.Analysis, @@ -76,8 +76,8 @@ def _iterate_selected_nodes(self): yield self.manifest.nodes[node] elif node in self.manifest.sources: yield self.manifest.sources[node] - elif node in self.manifest.reports: - yield self.manifest.reports[node] + elif node in self.manifest.exposures: + yield self.manifest.exposures[node] else: raise RuntimeException( f'Got an unexpected result from node selection: "{node}"' @@ -93,11 +93,11 @@ def generate_selectors(self): node.package_name, node.source_name, node.name ]) yield f'source:{source_selector}' - elif node.resource_type == NodeType.Report: - assert isinstance(node, ParsedReport) - # reports are searched for by pkg.report_name - report_selector = '.'.join([node.package_name, node.name]) - yield f'report:{report_selector}' + elif node.resource_type == NodeType.Exposure: + assert isinstance(node, ParsedExposure) + # exposures are searched for by pkg.exposure_name + exposure_selector = '.'.join([node.package_name, node.name]) + yield f'exposure:{exposure_selector}' else: # everything else is from `fqn` yield '.'.join(node.fqn) diff --git a/core/dbt/task/test.py b/core/dbt/task/test.py index 44fec0ac6f4..e83431f2101 100644 --- a/core/dbt/task/test.py +++ b/core/dbt/task/test.py @@ -115,7 +115,7 @@ def __init__(self, graph, manifest, previous_state): ) def expand_selection(self, selected: Set[UniqueId]) -> Set[UniqueId]: - # reports can't have tests, so this is relatively easy + # exposures can't have tests, so this is relatively easy selected_tests = set() for unique_id in self.graph.select_successors(selected): if unique_id in self.manifest.nodes: diff --git a/test/integration/007_graph_selection_tests/models/schema.yml b/test/integration/007_graph_selection_tests/models/schema.yml index b790dca4e6f..4444fb59953 100644 --- a/test/integration/007_graph_selection_tests/models/schema.yml +++ b/test/integration/007_graph_selection_tests/models/schema.yml @@ -23,15 +23,15 @@ sources: tables: - name: seed -reports: - - name: user_report +exposures: + - name: user_exposure type: dashboard depends_on: - ref('users') - ref('users_rollup') owner: email: nope@example.com - - name: seed_ml_report + - name: seed_ml_exposure type: ml depends_on: - source('raw', 'seed') diff --git a/test/integration/007_graph_selection_tests/test_graph_selection.py b/test/integration/007_graph_selection_tests/test_graph_selection.py index 5b38b1f24fd..ce4b79a6fe5 100644 --- a/test/integration/007_graph_selection_tests/test_graph_selection.py +++ b/test/integration/007_graph_selection_tests/test_graph_selection.py @@ -356,17 +356,17 @@ def test__postgres__concat_exclude_concat(self): assert results[0].node.name == 'unique_users_id' @use_profile('postgres') - def test__postgres__report_parents(self): + def test__postgres__exposure_parents(self): self.run_sql_file("seed.sql") - results = self.run_dbt(['ls', '--select', '+report:seed_ml_report']) + results = self.run_dbt(['ls', '--select', '+exposure:seed_ml_exposure']) assert len(results) == 2 - assert sorted(results) == ['report:test.seed_ml_report', 'source:test.raw.seed'] + assert sorted(results) == ['exposure:test.seed_ml_exposure', 'source:test.raw.seed'] - results = self.run_dbt(['ls', '--select', '1+report:user_report']) + results = self.run_dbt(['ls', '--select', '1+exposure:user_exposure']) assert len(results) == 3 - assert sorted(results) == ['report:test.user_report', 'test.users', 'test.users_rollup'] + assert sorted(results) == ['exposure:test.user_exposure', 'test.users', 'test.users_rollup'] - self.run_dbt(['run', '-m', '+report:user_report']) + self.run_dbt(['run', '-m', '+exposure:user_exposure']) # users, users_rollup assert len(results) == 3 diff --git a/test/integration/029_docs_generate_tests/models/schema.yml b/test/integration/029_docs_generate_tests/models/schema.yml index 9b5d87b2163..d9f49089ede 100644 --- a/test/integration/029_docs_generate_tests/models/schema.yml +++ b/test/integration/029_docs_generate_tests/models/schema.yml @@ -55,15 +55,15 @@ sources: description: "An ID field" -reports: - - name: simple_report +exposures: + - name: simple_exposure type: dashboard depends_on: - ref('model') - source('my_source', 'my_table') owner: email: something@example.com - - name: notebook_report + - name: notebook_exposure type: notebook depends_on: - ref('model') @@ -72,6 +72,6 @@ reports: email: something@example.com name: Some name description: > - A description of the complex report + A description of the complex exposure maturity: medium url: http://example.com/notebook/1 diff --git a/test/integration/029_docs_generate_tests/test_docs_generate.py b/test/integration/029_docs_generate_tests/test_docs_generate.py index d777704eb8d..397dd3737bc 100644 --- a/test/integration/029_docs_generate_tests/test_docs_generate.py +++ b/test/integration/029_docs_generate_tests/test_docs_generate.py @@ -1437,16 +1437,16 @@ def expected_seeded_manifest(self, model_database=None): 'fqn': ['test', 'my_source', 'my_table'], }, }, - 'reports': { - 'report.test.notebook_report': { + 'exposures': { + 'exposure.test.notebook_exposure': { 'depends_on': { 'macros': [], 'nodes': ['model.test.model', 'model.test.second_model'] }, - 'description': 'A description of the complex report', - 'fqn': ['test', 'notebook_report'], + 'description': 'A description of the complex exposure', + 'fqn': ['test', 'notebook_exposure'], 'maturity': 'medium', - 'name': 'notebook_report', + 'name': 'notebook_exposure', 'original_file_path': self.dir('models/schema.yml'), 'owner': { 'email': 'something@example.com', @@ -1455,14 +1455,14 @@ def expected_seeded_manifest(self, model_database=None): 'package_name': 'test', 'path': 'schema.yml', 'refs': [['model'], ['second_model']], - 'resource_type': 'report', + 'resource_type': 'exposure', 'root_path': self.test_root_realpath, 'sources': [], 'type': 'notebook', - 'unique_id': 'report.test.notebook_report', + 'unique_id': 'exposure.test.notebook_exposure', 'url': 'http://example.com/notebook/1' }, - 'report.test.simple_report': { + 'exposure.test.simple_exposure': { 'depends_on': { 'macros': [], 'nodes': [ @@ -1471,8 +1471,8 @@ def expected_seeded_manifest(self, model_database=None): ], }, 'description': None, - 'fqn': ['test', 'simple_report'], - 'name': 'simple_report', + 'fqn': ['test', 'simple_exposure'], + 'name': 'simple_exposure', 'original_file_path': self.dir('models/schema.yml'), 'owner': { 'email': 'something@example.com', @@ -1481,11 +1481,11 @@ def expected_seeded_manifest(self, model_database=None): 'package_name': 'test', 'path': 'schema.yml', 'refs': [['model']], - 'resource_type': 'report', + 'resource_type': 'exposure', 'root_path': self.test_root_realpath, 'sources': [['my_source', 'my_table']], 'type': 'dashboard', - 'unique_id': 'report.test.simple_report', + 'unique_id': 'exposure.test.simple_exposure', 'url': None, 'maturity': None, } @@ -1493,8 +1493,8 @@ def expected_seeded_manifest(self, model_database=None): 'parent_map': { 'model.test.model': ['seed.test.seed'], 'model.test.second_model': ['seed.test.seed'], - 'report.test.notebook_report': ['model.test.model', 'model.test.second_model'], - 'report.test.simple_report': ['model.test.model', 'source.test.my_source.my_table'], + 'exposure.test.notebook_exposure': ['model.test.model', 'model.test.second_model'], + 'exposure.test.simple_exposure': ['model.test.model', 'source.test.my_source.my_table'], 'seed.test.seed': [], 'source.test.my_source.my_table': [], 'test.test.not_null_model_id': ['model.test.model'], @@ -1503,17 +1503,17 @@ def expected_seeded_manifest(self, model_database=None): }, 'child_map': { 'model.test.model': [ - 'report.test.notebook_report', - 'report.test.simple_report', + 'exposure.test.notebook_exposure', + 'exposure.test.simple_exposure', 'test.test.not_null_model_id', 'test.test.test_nothing_model_', 'test.test.unique_model_id', ], - 'model.test.second_model': ['report.test.notebook_report'], - 'report.test.notebook_report': [], - 'report.test.simple_report': [], + 'model.test.second_model': ['exposure.test.notebook_exposure'], + 'exposure.test.notebook_exposure': [], + 'exposure.test.simple_exposure': [], 'seed.test.seed': ['model.test.model', 'model.test.second_model'], - 'source.test.my_source.my_table': ['report.test.simple_report'], + 'source.test.my_source.my_table': ['exposure.test.simple_exposure'], 'test.test.not_null_model_id': [], 'test.test.test_nothing_model_': [], 'test.test.unique_model_id': [], @@ -1871,7 +1871,7 @@ def expected_postgres_references_manifest(self, model_database=None): 'fqn': ['test', 'my_source', 'my_table'], }, }, - 'reports': {}, + 'exposures': {}, 'docs': { 'dbt.__overview__': ANY, 'test.column_info': { @@ -2431,7 +2431,7 @@ def expected_bigquery_complex_manifest(self): }, }, 'sources': {}, - 'reports': {}, + 'exposures': {}, 'child_map': { 'model.test.clustered': [], 'model.test.multi_clustered': [], @@ -2676,7 +2676,7 @@ def expected_redshift_incremental_view_manifest(self): }, }, 'sources': {}, - 'reports': {}, + 'exposures': {}, 'parent_map': { 'model.test.model': ['seed.test.seed'], 'seed.test.seed': [] @@ -2706,7 +2706,7 @@ def verify_manifest(self, expected_manifest): manifest_keys = frozenset({ 'nodes', 'sources', 'macros', 'parent_map', 'child_map', 'generated_at', - 'docs', 'metadata', 'docs', 'disabled', 'reports' + 'docs', 'metadata', 'docs', 'disabled', 'exposures' }) self.assertEqual(frozenset(manifest), manifest_keys) diff --git a/test/integration/062_defer_state_test/models/reports.yml b/test/integration/062_defer_state_test/models/exposures.yml similarity index 77% rename from test/integration/062_defer_state_test/models/reports.yml rename to test/integration/062_defer_state_test/models/exposures.yml index 19d52057d77..489dec3c3c4 100644 --- a/test/integration/062_defer_state_test/models/reports.yml +++ b/test/integration/062_defer_state_test/models/exposures.yml @@ -1,6 +1,6 @@ version: 2 -reports: - - name: my_report +exposures: + - name: my_exposure type: application depends_on: - ref('view_model') diff --git a/test/integration/062_defer_state_test/test_modified_state.py b/test/integration/062_defer_state_test/test_modified_state.py index 4c64c387cb5..34c5097aa75 100644 --- a/test/integration/062_defer_state_test/test_modified_state.py +++ b/test/integration/062_defer_state_test/test_modified_state.py @@ -72,7 +72,7 @@ def test_postgres_changed_seed_contents_state(self): results = self.run_dbt(['ls', '--select', 'state:modified+', '--state', './state']) assert len(results) == 7 - assert set(results) == {'test.seed', 'test.table_model', 'test.view_model', 'test.ephemeral_model', 'test.schema_test.not_null_view_model_id', 'test.schema_test.unique_view_model_id', 'report:test.my_report'} + assert set(results) == {'test.seed', 'test.table_model', 'test.view_model', 'test.ephemeral_model', 'test.schema_test.not_null_view_model_id', 'test.schema_test.unique_view_model_id', 'exposure:test.my_exposure'} shutil.rmtree('./state') self.copy_state() @@ -185,8 +185,8 @@ def test_postgres_changed_macro_contents(self): assert 'detected a change in macros' in stdout @use_profile('postgres') - def test_postgres_changed_report(self): - with open('models/reports.yml', 'a') as fp: + def test_postgres_changed_exposure(self): + with open('models/exposures.yml', 'a') as fp: fp.write(' name: John Doe\n') results, stdout = self.run_dbt_and_capture(['run', '--models', '+state:modified', '--state', './state']) diff --git a/test/unit/test_compiler.py b/test/unit/test_compiler.py index c25515eecc6..1063f2a0638 100644 --- a/test/unit/test_compiler.py +++ b/test/unit/test_compiler.py @@ -156,7 +156,7 @@ def test__prepend_ctes__already_has_cte(self): generated_at=datetime(2018, 2, 14, 9, 15, 13), disabled=[], files={}, - reports={}, + exposures={}, ) compiler = dbt.compilation.Compiler(self.config) @@ -241,7 +241,7 @@ def test__prepend_ctes__no_ctes(self): generated_at='2018-02-14T09:15:13Z', disabled=[], files={}, - reports={}, + exposures={}, ) compiler = dbt.compilation.Compiler(self.config) @@ -335,7 +335,7 @@ def test__prepend_ctes(self): generated_at='2018-02-14T09:15:13Z', disabled=[], files={}, - reports={}, + exposures={}, ) compiler = dbt.compilation.Compiler(self.config) @@ -440,7 +440,7 @@ def test__prepend_ctes__cte_not_compiled(self): generated_at='2018-02-14T09:15:13Z', disabled=[], files={}, - reports={}, + exposures={}, ) compiler = dbt.compilation.Compiler(self.config) @@ -546,7 +546,7 @@ def test__prepend_ctes__multiple_levels(self): generated_at='2018-02-14T09:15:13Z', disabled=[], files={}, - reports={}, + exposures={}, ) compiler = dbt.compilation.Compiler(self.config) diff --git a/test/unit/test_contracts_graph_parsed.py b/test/unit/test_contracts_graph_parsed.py index 0eeea70085c..aa85c7881b3 100644 --- a/test/unit/test_contracts_graph_parsed.py +++ b/test/unit/test_contracts_graph_parsed.py @@ -24,14 +24,14 @@ IntermediateSnapshotNode, ParsedNodePatch, ParsedMacro, - ParsedReport, + ParsedExposure, ParsedSeedNode, Docs, MacroDependsOn, ParsedSourceDefinition, ParsedDocumentation, ParsedHookNode, - ReportOwner, + ExposureOwner, TestMetadata, ) from dbt.contracts.graph.unparsed import ( @@ -1873,15 +1873,15 @@ def test_compare_changed_source_definition(func, basic_parsed_source_definition_ @pytest.fixture -def minimal_parsed_report_dict(): +def minimal_parsed_exposure_dict(): return { - 'name': 'my_report', + 'name': 'my_exposure', 'type': 'notebook', 'owner': { 'email': 'test@example.com', }, - 'fqn': ['test', 'reports', 'my_report'], - 'unique_id': 'report.test.my_report', + 'fqn': ['test', 'exposures', 'my_exposure'], + 'unique_id': 'exposure.test.my_exposure', 'package_name': 'test', 'path': 'models/something.yml', 'root_path': '/usr/src/app', @@ -1890,22 +1890,22 @@ def minimal_parsed_report_dict(): @pytest.fixture -def basic_parsed_report_dict(): +def basic_parsed_exposure_dict(): return { - 'name': 'my_report', + 'name': 'my_exposure', 'type': 'notebook', 'owner': { 'email': 'test@example.com', }, - 'resource_type': 'report', + 'resource_type': 'exposure', 'depends_on': { 'nodes': [], 'macros': [], }, 'refs': [], 'sources': [], - 'fqn': ['test', 'reports', 'my_report'], - 'unique_id': 'report.test.my_report', + 'fqn': ['test', 'exposures', 'my_exposure'], + 'unique_id': 'exposure.test.my_exposure', 'package_name': 'test', 'path': 'models/something.yml', 'root_path': '/usr/src/app', @@ -1914,30 +1914,30 @@ def basic_parsed_report_dict(): @pytest.fixture -def basic_parsed_report_object(): - return ParsedReport( - name='my_report', +def basic_parsed_exposure_object(): + return ParsedExposure( + name='my_exposure', type=ExposureType.Notebook, - fqn=['test', 'reports', 'my_report'], - unique_id='report.test.my_report', + fqn=['test', 'exposures', 'my_exposure'], + unique_id='exposure.test.my_exposure', package_name='test', path='models/something.yml', root_path='/usr/src/app', original_file_path='models/something.yml', - owner=ReportOwner(email='test@example.com'), + owner=ExposureOwner(email='test@example.com'), ) @pytest.fixture -def complex_parsed_report_dict(): +def complex_parsed_exposure_dict(): return { - 'name': 'my_report', + 'name': 'my_exposure', 'type': 'analysis', 'owner': { 'email': 'test@example.com', 'name': 'A Name', }, - 'resource_type': 'report', + 'resource_type': 'exposure', 'maturity': 'low', 'url': 'https://example.com/analyses/1', 'description': 'my description', @@ -1947,8 +1947,8 @@ def complex_parsed_report_dict(): }, 'refs': [], 'sources': [], - 'fqn': ['test', 'reports', 'my_report'], - 'unique_id': 'report.test.my_report', + 'fqn': ['test', 'exposures', 'my_exposure'], + 'unique_id': 'exposure.test.my_exposure', 'package_name': 'test', 'path': 'models/something.yml', 'root_path': '/usr/src/app', @@ -1957,17 +1957,17 @@ def complex_parsed_report_dict(): @pytest.fixture -def complex_parsed_report_object(): - return ParsedReport( - name='my_report', +def complex_parsed_exposure_object(): + return ParsedExposure( + name='my_exposure', type=ExposureType.Analysis, - owner=ReportOwner(email='test@example.com', name='A Name'), + owner=ExposureOwner(email='test@example.com', name='A Name'), maturity=MaturityType.Low, url='https://example.com/analyses/1', description='my description', depends_on=DependsOn(nodes=['models.test.my_model']), - fqn=['test', 'reports', 'my_report'], - unique_id='report.test.my_report', + fqn=['test', 'exposures', 'my_exposure'], + unique_id='exposure.test.my_exposure', package_name='test', path='models/something.yml', root_path='/usr/src/app', @@ -1975,22 +1975,22 @@ def complex_parsed_report_object(): ) -def test_basic_parsed_report(minimal_parsed_report_dict, basic_parsed_report_dict, basic_parsed_report_object): - assert_symmetric(basic_parsed_report_object, basic_parsed_report_dict, ParsedReport) - assert_from_dict(basic_parsed_report_object, minimal_parsed_report_dict, ParsedReport) - pickle.loads(pickle.dumps(basic_parsed_report_object)) +def test_basic_parsed_exposure(minimal_parsed_exposure_dict, basic_parsed_exposure_dict, basic_parsed_exposure_object): + assert_symmetric(basic_parsed_exposure_object, basic_parsed_exposure_dict, ParsedExposure) + assert_from_dict(basic_parsed_exposure_object, minimal_parsed_exposure_dict, ParsedExposure) + pickle.loads(pickle.dumps(basic_parsed_exposure_object)) -def test_complex_parsed_report(complex_parsed_report_dict, complex_parsed_report_object): - assert_symmetric(complex_parsed_report_object, complex_parsed_report_dict, ParsedReport) +def test_complex_parsed_exposure(complex_parsed_exposure_dict, complex_parsed_exposure_object): + assert_symmetric(complex_parsed_exposure_object, complex_parsed_exposure_dict, ParsedExposure) -unchanged_parsed_reports = [ +unchanged_parsed_exposures = [ lambda u: (u, u), ] -changed_parsed_reports = [ +changed_parsed_exposures = [ lambda u: (u, u.replace(fqn=u.fqn[:-1]+['something', u.fqn[-1]])), lambda u: (u, u.replace(type=ExposureType.ML)), lambda u: (u, u.replace(owner=u.owner.replace(name='My Name'))), @@ -2001,13 +2001,13 @@ def test_complex_parsed_report(complex_parsed_report_dict, complex_parsed_report ] -@pytest.mark.parametrize('func', unchanged_parsed_reports) -def test_compare_unchanged_parsed_report(func, basic_parsed_report_object): - node, compare = func(basic_parsed_report_object) +@pytest.mark.parametrize('func', unchanged_parsed_exposures) +def test_compare_unchanged_parsed_exposure(func, basic_parsed_exposure_object): + node, compare = func(basic_parsed_exposure_object) assert node.same_contents(compare) -@pytest.mark.parametrize('func', changed_parsed_reports) -def test_compare_changed_report(func, basic_parsed_report_object): - node, compare = func(basic_parsed_report_object) +@pytest.mark.parametrize('func', changed_parsed_exposures) +def test_compare_changed_exposure(func, basic_parsed_exposure_object): + node, compare = func(basic_parsed_exposure_object) assert not node.same_contents(compare) diff --git a/test/unit/test_contracts_graph_unparsed.py b/test/unit/test_contracts_graph_unparsed.py index 65e7d75a8a5..0ca5a749928 100644 --- a/test/unit/test_contracts_graph_unparsed.py +++ b/test/unit/test_contracts_graph_unparsed.py @@ -6,7 +6,7 @@ UnparsedNode, UnparsedRunHook, UnparsedMacro, Time, TimePeriod, FreshnessStatus, FreshnessThreshold, Quoting, UnparsedSourceDefinition, UnparsedSourceTableDefinition, UnparsedDocumentationFile, UnparsedColumn, - UnparsedNodeUpdate, Docs, UnparsedReport, MaturityType, ReportOwner, + UnparsedNodeUpdate, Docs, UnparsedExposure, MaturityType, ExposureOwner, ExposureType ) from dbt.node_types import NodeType @@ -571,19 +571,19 @@ def test_bad_test_type(self): self.assert_fails_validation(dct) -class TestUnparsedReport(ContractTestCase): - ContractType = UnparsedReport +class TestUnparsedExposure(ContractTestCase): + ContractType = UnparsedExposure def get_ok_dict(self): return { - 'name': 'my_report', + 'name': 'my_exposure', 'type': 'dashboard', 'owner': { 'email': 'name@example.com', }, 'maturity': 'medium', 'url': 'https://example.com/dashboards/1', - 'description': 'A report', + 'description': 'A exposure', 'depends_on': [ 'ref("my_model")', 'source("raw", "source_table")', @@ -591,18 +591,18 @@ def get_ok_dict(self): } def test_ok(self): - report = self.ContractType( - name='my_report', + exposure = self.ContractType( + name='my_exposure', type=ExposureType.Dashboard, - owner=ReportOwner(email='name@example.com'), + owner=ExposureOwner(email='name@example.com'), maturity=MaturityType.Medium, url='https://example.com/dashboards/1', - description='A report', + description='A exposure', depends_on=['ref("my_model")', 'source("raw", "source_table")'], ) dct = self.get_ok_dict() - self.assert_symmetric(report, dct) - pickle.loads(pickle.dumps(report)) + self.assert_symmetric(exposure, dct) + pickle.loads(pickle.dumps(exposure)) def test_ok_exposures(self): for exposure_allowed in ('dashboard', 'notebook', 'analysis', 'ml', 'application'): diff --git a/test/unit/test_graph_selector_methods.py b/test/unit/test_graph_selector_methods.py index 007eaa3875f..6f33943cbae 100644 --- a/test/unit/test_graph_selector_methods.py +++ b/test/unit/test_graph_selector_methods.py @@ -10,7 +10,7 @@ DependsOn, NodeConfig, ParsedModelNode, - ParsedReport, + ParsedExposure, ParsedSeedNode, ParsedSnapshotNode, ParsedDataTestNode, @@ -21,7 +21,7 @@ ColumnInfo, ) from dbt.contracts.graph.manifest import Manifest -from dbt.contracts.graph.unparsed import ExposureType, ReportOwner +from dbt.contracts.graph.unparsed import ExposureType, ExposureOwner from dbt.contracts.state import PreviousState from dbt.node_types import NodeType from dbt.graph.selector_methods import ( @@ -35,7 +35,7 @@ TestNameSelectorMethod, TestTypeSelectorMethod, StateSelectorMethod, - ReportSelectorMethod, + ExposureSelectorMethod, ) import dbt.exceptions import dbt.contracts.graph.parsed @@ -294,7 +294,7 @@ def make_data_test(pkg, name, sql, refs=None, sources=None, tags=None, path=None ) -def make_report(pkg, name, path=None, fqn_extras=None, owner=None): +def make_exposure(pkg, name, path=None, fqn_extras=None, owner=None): if path is None: path = 'schema.yml' @@ -302,14 +302,14 @@ def make_report(pkg, name, path=None, fqn_extras=None, owner=None): fqn_extras = [] if owner is None: - owner = ReportOwner(email='test@example.com') + owner = ExposureOwner(email='test@example.com') - fqn = [pkg, 'reports'] + fqn_extras + [name] - return ParsedReport( + fqn = [pkg, 'exposures'] + fqn_extras + [name] + return ParsedExposure( name=name, type=ExposureType.Notebook, fqn=fqn, - unique_id=f'report.{pkg}.{name}', + unique_id=f'exposure.{pkg}.{name}', package_name=pkg, path=path, root_path='/usr/src/app', @@ -468,7 +468,7 @@ def manifest(seed, source, ephemeral_model, view_model, table_model, ext_source, macros={}, docs={}, files={}, - reports={}, + exposures={}, generated_at=datetime.utcnow(), disabled=[], ) @@ -476,7 +476,7 @@ def manifest(seed, source, ephemeral_model, view_model, table_model, ext_source, def search_manifest_using_method(manifest, method, selection): - selected = method.search(set(manifest.nodes) | set(manifest.sources) | set(manifest.reports), selection) + selected = method.search(set(manifest.nodes) | set(manifest.sources) | set(manifest.exposures), selection) results = {manifest.expect(uid).search_name for uid in selected} return results @@ -586,14 +586,14 @@ def test_select_test_type(manifest): assert search_manifest_using_method(manifest, method, 'data') == {'view_test_nothing'} -def test_select_report(manifest): - report = make_report('test', 'my_report') - manifest.reports[report.unique_id] = report +def test_select_exposure(manifest): + exposure = make_exposure('test', 'my_exposure') + manifest.exposures[exposure.unique_id] = exposure methods = MethodManager(manifest, None) - method = methods.get_method('report', []) - assert isinstance(method, ReportSelectorMethod) - assert search_manifest_using_method(manifest, method, 'my_report') == {'my_report'} - assert not search_manifest_using_method(manifest, method, 'not_my_report') + method = methods.get_method('exposure', []) + assert isinstance(method, ExposureSelectorMethod) + assert search_manifest_using_method(manifest, method, 'my_exposure') == {'my_exposure'} + assert not search_manifest_using_method(manifest, method, 'not_my_exposure') @pytest.fixture diff --git a/test/unit/test_manifest.py b/test/unit/test_manifest.py index 49f856f8d52..1a0d25e4bf0 100644 --- a/test/unit/test_manifest.py +++ b/test/unit/test_manifest.py @@ -215,14 +215,14 @@ def setUp(self): def test__no_nodes(self): manifest = Manifest(nodes={}, sources={}, macros={}, docs={}, generated_at=datetime.utcnow(), disabled=[], - files={}, reports={}) + files={}, exposures={}) self.assertEqual( manifest.writable_manifest().to_dict(), { 'nodes': {}, 'sources': {}, 'macros': {}, - 'reports': {}, + 'exposures': {}, 'parent_map': {}, 'child_map': {}, 'generated_at': '2018-02-14T09:15:13Z', @@ -237,7 +237,7 @@ def test__nested_nodes(self): nodes = copy.copy(self.nested_nodes) manifest = Manifest(nodes=nodes, sources={}, macros={}, docs={}, generated_at=datetime.utcnow(), disabled=[], - files={}, reports={}) + files={}, exposures={}) serialized = manifest.writable_manifest().to_dict() self.assertEqual(serialized['generated_at'], '2018-02-14T09:15:13Z') self.assertEqual(serialized['docs'], {}) @@ -303,7 +303,7 @@ def test__build_flat_graph(self): sources = copy.copy(self.sources) manifest = Manifest(nodes=nodes, sources=sources, macros={}, docs={}, generated_at=datetime.utcnow(), disabled=[], - files={}, reports={}) + files={}, exposures={}) manifest.build_flat_graph() flat_graph = manifest.flat_graph flat_nodes = flat_graph['nodes'] @@ -342,7 +342,7 @@ def test_no_nodes_with_metadata(self, mock_user): ) manifest = Manifest(nodes={}, sources={}, macros={}, docs={}, generated_at=datetime.utcnow(), disabled=[], - metadata=metadata, files={}, reports={}) + metadata=metadata, files={}, exposures={}) self.assertEqual( manifest.writable_manifest().to_dict(), @@ -350,7 +350,7 @@ def test_no_nodes_with_metadata(self, mock_user): 'nodes': {}, 'sources': {}, 'macros': {}, - 'reports': {}, + 'exposures': {}, 'parent_map': {}, 'child_map': {}, 'generated_at': '2018-02-14T09:15:13Z', @@ -368,7 +368,7 @@ def test_no_nodes_with_metadata(self, mock_user): def test_get_resource_fqns_empty(self): manifest = Manifest(nodes={}, sources={}, macros={}, docs={}, generated_at=datetime.utcnow(), disabled=[], - files={}, reports={}) + files={}, exposures={}) self.assertEqual(manifest.get_resource_fqns(), {}) def test_get_resource_fqns(self): @@ -395,7 +395,7 @@ def test_get_resource_fqns(self): ) manifest = Manifest(nodes=nodes, sources=self.sources, macros={}, docs={}, generated_at=datetime.utcnow(), disabled=[], - files={}, reports={}) + files={}, exposures={}) expect = { 'models': frozenset([ ('snowplow', 'events'), @@ -577,14 +577,14 @@ def setUp(self): def test__no_nodes(self): manifest = Manifest(nodes={}, sources={}, macros={}, docs={}, generated_at=datetime.utcnow(), disabled=[], - files={}, reports={}) + files={}, exposures={}) self.assertEqual( manifest.writable_manifest().to_dict(), { 'nodes': {}, 'macros': {}, 'sources': {}, - 'reports': {}, + 'exposures': {}, 'parent_map': {}, 'child_map': {}, 'generated_at': '2018-02-14T09:15:13Z', @@ -599,7 +599,7 @@ def test__nested_nodes(self): nodes = copy.copy(self.nested_nodes) manifest = Manifest(nodes=nodes, sources={}, macros={}, docs={}, generated_at=datetime.utcnow(), disabled=[], - files={}, reports={}) + files={}, exposures={}) serialized = manifest.writable_manifest().to_dict() self.assertEqual(serialized['generated_at'], '2018-02-14T09:15:13Z') self.assertEqual(serialized['disabled'], []) @@ -663,7 +663,7 @@ def test__build_flat_graph(self): nodes = copy.copy(self.nested_nodes) manifest = Manifest(nodes=nodes, sources={}, macros={}, docs={}, generated_at=datetime.utcnow(), disabled=[], - files={}, reports={}) + files={}, exposures={}) manifest.build_flat_graph() flat_graph = manifest.flat_graph flat_nodes = flat_graph['nodes'] @@ -711,7 +711,7 @@ def setUp(self): generated_at=datetime.utcnow(), disabled=[], files={}, - reports={}, + exposures={}, ) @@ -732,7 +732,7 @@ def make_manifest(nodes=[], sources=[], macros=[], docs=[]): generated_at=datetime.utcnow(), disabled=[], files={}, - reports={}, + exposures={}, ) diff --git a/test/unit/test_parser.py b/test/unit/test_parser.py index 51362ddeefc..d9cb03ee03c 100644 --- a/test/unit/test_parser.py +++ b/test/unit/test_parser.py @@ -834,7 +834,7 @@ def setUp(self): self.doc.unique_id: self.doc, } self.manifest = Manifest( - nodes=nodes, sources=sources, macros={}, docs=docs, disabled=[], files={}, reports={}, generated_at=mock.MagicMock() + nodes=nodes, sources=sources, macros={}, docs=docs, disabled=[], files={}, exposures={}, generated_at=mock.MagicMock() ) def test_process_docs(self):