From 518eb73f883308fc8562a2c131a2ad7d788e9358 Mon Sep 17 00:00:00 2001 From: Quigley Malcolm Date: Fri, 28 Jul 2023 14:36:51 -0700 Subject: [PATCH] [CT-2888] Support dbt-semantic-interfaces 0.2.0 (#8250) * Upgrade DSI dependency to ~=0.2.0 * Allow users to specify `primary_entity` on semantic models * Add `primary_entity` and `primary_entity_reference` to SemanticModel node * Plumb primary_entity from unparsed to parsed semantic nodes * Fix metric filter specifications in existing tests * Add changie doc about supporting DSI 0.2.0 --- .changes/unreleased/Dependencies-20230728-135227.yaml | 6 ++++++ core/dbt/contracts/graph/nodes.py | 10 ++++++++++ core/dbt/contracts/graph/unparsed.py | 1 + core/dbt/parser/schema_yaml_readers.py | 1 + core/setup.py | 3 +-- tests/functional/duplicates/test_duplicate_metric.py | 2 +- tests/functional/metrics/fixtures.py | 10 +++++----- tests/functional/partial_parsing/fixtures.py | 8 ++++---- .../semantic_models/test_semantic_model_parsing.py | 2 ++ .../test_semantic_layer_nodes_satisfy_protocols.py | 2 +- tests/unit/test_yaml_renderer.py | 4 ++-- 11 files changed, 34 insertions(+), 15 deletions(-) create mode 100644 .changes/unreleased/Dependencies-20230728-135227.yaml diff --git a/.changes/unreleased/Dependencies-20230728-135227.yaml b/.changes/unreleased/Dependencies-20230728-135227.yaml new file mode 100644 index 00000000000..3373d055c3e --- /dev/null +++ b/.changes/unreleased/Dependencies-20230728-135227.yaml @@ -0,0 +1,6 @@ +kind: Dependencies +body: Support dbt-semantic-interfaces 0.2.0 +time: 2023-07-28T13:52:27.207241-07:00 +custom: + Author: QMalcolm + PR: "8250" diff --git a/core/dbt/contracts/graph/nodes.py b/core/dbt/contracts/graph/nodes.py index 3e191e90855..59ac576db79 100644 --- a/core/dbt/contracts/graph/nodes.py +++ b/core/dbt/contracts/graph/nodes.py @@ -50,6 +50,7 @@ from dbt.node_types import ModelLanguage, NodeType, AccessType from dbt_semantic_interfaces.call_parameter_sets import FilterCallParameterSets from dbt_semantic_interfaces.references import ( + EntityReference, MeasureReference, LinkableElementReference, SemanticModelReference, @@ -1498,6 +1499,7 @@ class SemanticModel(GraphNode): refs: List[RefArgs] = field(default_factory=list) created_at: float = field(default_factory=lambda: time.time()) config: SemanticModelConfig = field(default_factory=SemanticModelConfig) + primary_entity: Optional[str] = None @property def entity_references(self) -> List[LinkableElementReference]: @@ -1580,6 +1582,14 @@ def checked_agg_time_dimension_for_measure( ) return TimeDimensionReference(element_name=agg_time_dimension_name) + @property + def primary_entity_reference(self) -> Optional[EntityReference]: + return ( + EntityReference(element_name=self.primary_entity) + if self.primary_entity is not None + else None + ) + # ==================================== # Patches diff --git a/core/dbt/contracts/graph/unparsed.py b/core/dbt/contracts/graph/unparsed.py index babbca64a21..585ac9cc3e0 100644 --- a/core/dbt/contracts/graph/unparsed.py +++ b/core/dbt/contracts/graph/unparsed.py @@ -728,6 +728,7 @@ class UnparsedSemanticModel(dbtClassMixin): entities: List[UnparsedEntity] = field(default_factory=list) measures: List[UnparsedMeasure] = field(default_factory=list) dimensions: List[UnparsedDimension] = field(default_factory=list) + primary_entity: Optional[str] = None def normalize_date(d: Optional[datetime.date]) -> Optional[datetime.datetime]: diff --git a/core/dbt/parser/schema_yaml_readers.py b/core/dbt/parser/schema_yaml_readers.py index 8f7b1519fb6..2f2a3eb18e6 100644 --- a/core/dbt/parser/schema_yaml_readers.py +++ b/core/dbt/parser/schema_yaml_readers.py @@ -532,6 +532,7 @@ def parse_semantic_model(self, unparsed: UnparsedSemanticModel): measures=self._get_measures(unparsed.measures), dimensions=self._get_dimensions(unparsed.dimensions), defaults=unparsed.defaults, + primary_entity=unparsed.primary_entity, ) ctx = generate_parse_semantic_models( diff --git a/core/setup.py b/core/setup.py index 9f3b51eddae..e198bc0aed2 100644 --- a/core/setup.py +++ b/core/setup.py @@ -75,8 +75,7 @@ "hologram~=0.0.16", # includes transitive dependencies on python-dateutil and jsonschema "minimal-snowplow-tracker~=0.0.2", # DSI is under active development, so we're pinning to specific dev versions for now. - # TODO: Before RC/final release, update to use ~= pinning. - "dbt-semantic-interfaces~=0.1.0rc1", + "dbt-semantic-interfaces~=0.2.0", # ---- # Expect compatibility with all new versions of these packages, so lower bounds only. "packaging>20.9", diff --git a/tests/functional/duplicates/test_duplicate_metric.py b/tests/functional/duplicates/test_duplicate_metric.py index 3a9c3205796..a5f6b60e8f3 100644 --- a/tests/functional/duplicates/test_duplicate_metric.py +++ b/tests/functional/duplicates/test_duplicate_metric.py @@ -25,7 +25,7 @@ type_params: measure: name: "years_tenure" - filter: "{{dimension('loves_dbt')}} is true" + filter: "{{ Dimension('people_entity__loves_dbt') }} is true" """ diff --git a/tests/functional/metrics/fixtures.py b/tests/functional/metrics/fixtures.py index 87fc67c10c6..65d61ad74ad 100644 --- a/tests/functional/metrics/fixtures.py +++ b/tests/functional/metrics/fixtures.py @@ -70,7 +70,7 @@ type_params: measure: name: "years_tenure" - filter: "{{dimension('loves_dbt')}} is true" + filter: "{{ Dimension('id__loves_dbt') }} is true" - name: average_tenure label: "Average tenure" @@ -115,7 +115,7 @@ type_params: measure: name: years_tenure - filter: "{{dimension('loves_dbt')}} is true" + filter: "{{ Dimension('id__loves_dbt') }} is true" - name: collective_window label: "Collective window" @@ -124,7 +124,7 @@ type_params: measure: name: years_tenure - filter: "{{dimension('loves_dbt')}} is true" + filter: "{{ Dimension('id__loves_dbt') }} is true" window: 14 days - name: average_tenure @@ -452,7 +452,7 @@ type_params: measure: name: years_tenure - filter: "{{dimension('loves_dbt')}} is true" + filter: "{{ Dimension('id__loves_dbt') }} is true" """ @@ -479,7 +479,7 @@ type_params: measure: name: years_tenure - filter: "{{dimension('loves_dbt')}} is true" + filter: "{{ Dimension('id__loves_dbt') }} is true" """ diff --git a/tests/functional/partial_parsing/fixtures.py b/tests/functional/partial_parsing/fixtures.py index 9da5a6f5025..52578d90308 100644 --- a/tests/functional/partial_parsing/fixtures.py +++ b/tests/functional/partial_parsing/fixtures.py @@ -353,7 +353,7 @@ type_params: measure: name: customers - filter: "{{dimension('loves_dbt')}} is true" + filter: "{{ Dimension('id__loves_dbt') }} is true" +meta: is_okr: True tags: @@ -472,7 +472,7 @@ type_params: measure: name: years_tenure - filter: "{{dimension('loves_dbt')}} is true" + filter: "{{ Dimension('id__loves_dbt') }} is true" """ @@ -619,7 +619,7 @@ type_params: measure: name: years_tenure - filter: "{{dimension('loves_dbt')}} is true" + filter: "{{ Dimension('id__loves_dbt') }} is true" """ @@ -1008,7 +1008,7 @@ type_params: measure: name: years_tenure - filter: "{{dimension('loves_dbt')}} is true" + filter: "{{ Dimension('id__loves_dbt') }} is true" """ diff --git a/tests/functional/semantic_models/test_semantic_model_parsing.py b/tests/functional/semantic_models/test_semantic_model_parsing.py index fbe21749772..6b0fe643691 100644 --- a/tests/functional/semantic_models/test_semantic_model_parsing.py +++ b/tests/functional/semantic_models/test_semantic_model_parsing.py @@ -61,6 +61,8 @@ - name: user type: foreign expr: user_id + - name: id + type: primary metrics: - name: records_with_revenue diff --git a/tests/unit/test_semantic_layer_nodes_satisfy_protocols.py b/tests/unit/test_semantic_layer_nodes_satisfy_protocols.py index 4d7618b39b9..083924fb428 100644 --- a/tests/unit/test_semantic_layer_nodes_satisfy_protocols.py +++ b/tests/unit/test_semantic_layer_nodes_satisfy_protocols.py @@ -169,7 +169,7 @@ def test_metric_node_satisfies_protocol(): def test_where_filter_satisfies_protocol(): where_filter = WhereFilter( - where_sql_template="{{ dimension('dimension_name') }} AND {{ time_dimension('time_dimension_name', 'month') }} AND {{ entity('entity_name') }}" + where_sql_template="{{ Dimension('enity_name__dimension_name') }} AND {{ TimeDimension('entity_name__time_dimension_name', 'month') }} AND {{ Entity('entity_name') }}" ) assert isinstance(where_filter, RuntimeCheckableWhereFilter) diff --git a/tests/unit/test_yaml_renderer.py b/tests/unit/test_yaml_renderer.py index 783cc69905e..9a703617aea 100644 --- a/tests/unit/test_yaml_renderer.py +++ b/tests/unit/test_yaml_renderer.py @@ -105,14 +105,14 @@ def test__metrics(self): dct = { "name": "test{{ metric_name_end }}", "description": "{{ docs('my_doc') }}", - "filter": "{{ dimension('my_dim') }} = false", + "filter": "{{ Dimension('my_entity__my_dim') }} = false", } # We expect the expression and description will not be rendered, but # other fields will be expected = { "name": "test_metric", "description": "{{ docs('my_doc') }}", - "filter": "{{ dimension('my_dim') }} = false", + "filter": "{{ Dimension('my_entity__my_dim') }} = false", } dct = renderer.render_data(dct) self.assertEqual(dct, expected)