diff --git a/CHANGELOG.md b/CHANGELOG.md index 514cb2ab2f6..0bfb14b05d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ ## dbt 0.19.0 (Release TBD) +### Fixes +- Fix regression with defining exposures and other resources with the same name ([#2969](https://github.com/fishtown-analytics/dbt/issues/2969), [#3009](https://github.com/fishtown-analytics/dbt/pull/3009)) + ### Under the hood - Rewrite macro for snapshot_merge_sql to make compatible with other SQL dialects ([#3003](https://github.com/fishtown-analytics/dbt/pull/3003) - Rewrite logic in `snapshot_check_strategy()` to make compatible with other SQL dialects ([#3000](https://github.com/fishtown-analytics/dbt/pull/3000), [#3001](https://github.com/fishtown-analytics/dbt/pull/3001)) diff --git a/core/dbt/contracts/graph/unparsed.py b/core/dbt/contracts/graph/unparsed.py index 70eb95710b8..6d2fcf3a508 100644 --- a/core/dbt/contracts/graph/unparsed.py +++ b/core/dbt/contracts/graph/unparsed.py @@ -406,7 +406,7 @@ class ExposureOwner(JsonSchemaMixin, Replaceable): @dataclass -class UnparsedExposure(HasYamlMetadata, Replaceable): +class UnparsedExposure(JsonSchemaMixin, Replaceable): name: str type: ExposureType owner: ExposureOwner diff --git a/core/dbt/parser/schemas.py b/core/dbt/parser/schemas.py index 74c7228e424..128842441e4 100644 --- a/core/dbt/parser/schemas.py +++ b/core/dbt/parser/schemas.py @@ -95,6 +95,7 @@ def error_context( class ParserRef: """A helper object to hold parse-time references.""" + def __init__(self): self.column_info: Dict[str, ColumnInfo] = {} @@ -584,6 +585,10 @@ def parse_file(self, block: FileBlock) -> None: parser = MacroPatchParser(self, yaml_block, plural) elif key == NodeType.Analysis: parser = AnalysisPatchParser(self, yaml_block, plural) + elif key == NodeType.Exposure: + # handle exposures separately, but they are + # technically still "documentable" + continue else: parser = TestablePatchParser(self, yaml_block, plural) for test_block in parser.parse(): diff --git a/test/integration/025_duplicate_model_test/models-exposure-dupes/schema.yml b/test/integration/025_duplicate_model_test/models-exposure-dupes/schema.yml new file mode 100644 index 00000000000..db68dec79e8 --- /dev/null +++ b/test/integration/025_duplicate_model_test/models-exposure-dupes/schema.yml @@ -0,0 +1,10 @@ +version: 2 +exposures: + - name: something + type: dashboard + owner: + email: test@example.com + - name: something + type: dashboard + owner: + email: test@example.com diff --git a/test/integration/025_duplicate_model_test/models-naming-dupes/schema.yml b/test/integration/025_duplicate_model_test/models-naming-dupes/schema.yml new file mode 100644 index 00000000000..a643b52cc8f --- /dev/null +++ b/test/integration/025_duplicate_model_test/models-naming-dupes/schema.yml @@ -0,0 +1,9 @@ +version: 2 +models: + - name: something + description: This table has basic information about orders, as well as some derived facts based on payments +exposures: + - name: something + type: dashboard + owner: + email: test@example.com diff --git a/test/integration/025_duplicate_model_test/test_duplicate_exposure.py b/test/integration/025_duplicate_model_test/test_duplicate_exposure.py new file mode 100644 index 00000000000..33bcb3b7892 --- /dev/null +++ b/test/integration/025_duplicate_model_test/test_duplicate_exposure.py @@ -0,0 +1,23 @@ +from dbt.exceptions import CompilationException +from test.integration.base import DBTIntegrationTest, use_profile + + +class TestDuplicateExposure(DBTIntegrationTest): + + @property + def schema(self): + return "duplicate_exposure_025" + + @property + def models(self): + return "models-exposure-dupes" + + @use_profile("postgres") + def test_postgres_duplicate_exposure(self): + message = "dbt found two resources with the name" + try: + self.run_dbt(["compile"]) + self.assertTrue(False, "dbt did not throw for duplicate exposures") + except CompilationException as e: + self.assertTrue(message in str( + e), "dbt did not throw the correct error message") diff --git a/test/integration/025_duplicate_model_test/test_duplicate_model.py b/test/integration/025_duplicate_model_test/test_duplicate_model.py index 42dad92dfc0..63177085650 100644 --- a/test/integration/025_duplicate_model_test/test_duplicate_model.py +++ b/test/integration/025_duplicate_model_test/test_duplicate_model.py @@ -12,26 +12,6 @@ def schema(self): def models(self): return "models-1" - @property - def profile_config(self): - return { - "test": { - "outputs": { - "dev": { - "type": "postgres", - "threads": 1, - "host": self.database_host, - "port": 5432, - "user": "root", - "pass": "password", - "dbname": "dbt", - "schema": self.unique_schema() - }, - }, - "target": "dev" - } - } - @use_profile("postgres") def test_postgres_duplicate_model_enabled(self): message = "dbt found two resources with the name" @@ -39,7 +19,8 @@ def test_postgres_duplicate_model_enabled(self): self.run_dbt(["run"]) self.assertTrue(False, "dbt did not throw for duplicate models") except CompilationException as e: - self.assertTrue(message in str(e), "dbt did not throw the correct error message") + self.assertTrue(message in str( + e), "dbt did not throw the correct error message") class TestDuplicateModelDisabled(DBTIntegrationTest): @@ -52,26 +33,6 @@ def schema(self): def models(self): return "models-2" - @property - def profile_config(self): - return { - "test": { - "outputs": { - "dev": { - "type": "postgres", - "threads": 1, - "host": self.database_host, - "port": 5432, - "user": "root", - "pass": "password", - "dbname": "dbt", - "schema": self.unique_schema() - }, - }, - "target": "dev" - } - } - @use_profile("postgres") def test_postgres_duplicate_model_disabled(self): try: @@ -124,7 +85,8 @@ def test_postgres_duplicate_model_enabled_across_packages(self): self.run_dbt(["run"]) self.assertTrue(False, "dbt did not throw for duplicate models") except CompilationException as e: - self.assertTrue(message in str(e), "dbt did not throw the correct error message") + self.assertTrue(message in str( + e), "dbt did not throw the correct error message") class TestDuplicateModelDisabledAcrossPackages(DBTIntegrationTest): diff --git a/test/integration/025_duplicate_model_test/test_duplicate_resource.py b/test/integration/025_duplicate_model_test/test_duplicate_resource.py new file mode 100644 index 00000000000..0e738dc7b3b --- /dev/null +++ b/test/integration/025_duplicate_model_test/test_duplicate_resource.py @@ -0,0 +1,21 @@ +from dbt.exceptions import CompilationException +from test.integration.base import DBTIntegrationTest, use_profile + + +class TestDuplicateSchemaResource(DBTIntegrationTest): + + @property + def schema(self): + return "duplicate_resource_025" + + @property + def models(self): + return "models-naming-dupes-1" + + @use_profile("postgres") + def test_postgres_duplicate_model_and_exposure(self): + try: + self.run_dbt(["compile"]) + except CompilationException: + self.fail("Compilation Exception raised on model and " + "exposure with the same name") diff --git a/test/integration/025_duplicate_model_test/test_duplicate_source.py b/test/integration/025_duplicate_model_test/test_duplicate_source.py index a0ae261fa8e..614f600f0c7 100644 --- a/test/integration/025_duplicate_model_test/test_duplicate_source.py +++ b/test/integration/025_duplicate_model_test/test_duplicate_source.py @@ -12,31 +12,12 @@ def schema(self): def models(self): return "models-source-dupes" - @property - def profile_config(self): - return { - "test": { - "outputs": { - "dev": { - "type": "postgres", - "threads": 1, - "host": self.database_host, - "port": 5432, - "user": "root", - "pass": "password", - "dbname": "dbt", - "schema": self.unique_schema() - }, - }, - "target": "dev" - } - } - @use_profile("postgres") - def test_postgres_duplicate_model_enabled(self): + def test_postgres_duplicate_source_enabled(self): message = "dbt found two resources with the name" try: self.run_dbt(["compile"]) self.assertTrue(False, "dbt did not throw for duplicate sources") except CompilationException as e: - self.assertTrue(message in str(e), "dbt did not throw the correct error message") + self.assertTrue(message in str( + e), "dbt did not throw the correct error message") diff --git a/test/unit/test_contracts_graph_unparsed.py b/test/unit/test_contracts_graph_unparsed.py index 100b65020f6..87007147497 100644 --- a/test/unit/test_contracts_graph_unparsed.py +++ b/test/unit/test_contracts_graph_unparsed.py @@ -579,7 +579,6 @@ class TestUnparsedExposure(ContractTestCase): def get_ok_dict(self): return { - 'yaml_key': 'exposures', 'name': 'my_exposure', 'type': 'dashboard', 'owner': { @@ -592,13 +591,10 @@ def get_ok_dict(self): 'ref("my_model")', 'source("raw", "source_table")', ], - 'original_file_path': '/some/fake/path', - 'package_name': 'test' } def test_ok(self): exposure = self.ContractType( - yaml_key='exposures', name='my_exposure', type=ExposureType.Dashboard, owner=ExposureOwner(email='name@example.com'), @@ -606,8 +602,6 @@ def test_ok(self): url='https://example.com/dashboards/1', description='A exposure', depends_on=['ref("my_model")', 'source("raw", "source_table")'], - original_file_path='/some/fake/path', - package_name='test' ) dct = self.get_ok_dict() self.assert_symmetric(exposure, dct)