From e155c0d5ddad15f7787acc2b142055f507438052 Mon Sep 17 00:00:00 2001 From: Devon Fulcher <24593113+DevonFulcher@users.noreply.github.com> Date: Wed, 23 Oct 2024 12:01:08 -0500 Subject: [PATCH] Add meta to measures, entities, and dimensions --- .../unreleased/Features-20241023-113450.yaml | 6 ++++++ .../implementations/elements/dimension.py | 18 +++++++++++++++++- .../implementations/elements/entity.py | 18 +++++++++++++++++- .../implementations/elements/measure.py | 17 ++++++++++++++++- dbt_semantic_interfaces/protocols/dimension.py | 17 ++++++++++++++++- dbt_semantic_interfaces/protocols/entity.py | 17 ++++++++++++++++- dbt_semantic_interfaces/protocols/measure.py | 17 ++++++++++++++++- tests/parsing/test_semantic_model_parsing.py | 11 +++++++++-- tests/test_implements_satisfy_protocols.py | 11 ++++++++++- 9 files changed, 123 insertions(+), 9 deletions(-) create mode 100644 .changes/unreleased/Features-20241023-113450.yaml diff --git a/.changes/unreleased/Features-20241023-113450.yaml b/.changes/unreleased/Features-20241023-113450.yaml new file mode 100644 index 00000000..0eda8b80 --- /dev/null +++ b/.changes/unreleased/Features-20241023-113450.yaml @@ -0,0 +1,6 @@ +kind: Features +body: Support meta for dimensions, entities, and measures +time: 2024-10-23T11:34:50.577294-05:00 +custom: + Author: DevonFulcher + Issue: None diff --git a/dbt_semantic_interfaces/implementations/elements/dimension.py b/dbt_semantic_interfaces/implementations/elements/dimension.py index c3cfa2e4..c75d6852 100644 --- a/dbt_semantic_interfaces/implementations/elements/dimension.py +++ b/dbt_semantic_interfaces/implementations/elements/dimension.py @@ -1,12 +1,17 @@ from __future__ import annotations -from typing import Optional +from typing import Any, Dict, Optional + +from pydantic import Field +from typing_extensions import override from dbt_semantic_interfaces.implementations.base import ( HashableBaseModel, ModelWithMetadataParsing, ) from dbt_semantic_interfaces.implementations.metadata import PydanticMetadata +from dbt_semantic_interfaces.protocols.dimension import DimensionConfig +from dbt_semantic_interfaces.protocols.protocol_hint import ProtocolHint from dbt_semantic_interfaces.references import ( DimensionReference, TimeDimensionReference, @@ -37,6 +42,16 @@ class PydanticDimensionTypeParams(HashableBaseModel): validity_params: Optional[PydanticDimensionValidityParams] = None +class PydanticDimensionConfig(HashableBaseModel, ProtocolHint[DimensionConfig]): + """PydanticDimension config.""" + + @override + def _implements_protocol(self) -> DimensionConfig: # noqa: D + return self + + meta: Dict[str, Any] = Field(default_factory=dict) + + class PydanticDimension(HashableBaseModel, ModelWithMetadataParsing): """Describes a dimension.""" @@ -48,6 +63,7 @@ class PydanticDimension(HashableBaseModel, ModelWithMetadataParsing): expr: Optional[str] = None metadata: Optional[PydanticMetadata] label: Optional[str] = None + config: Optional[PydanticDimensionConfig] @property def reference(self) -> DimensionReference: # noqa: D diff --git a/dbt_semantic_interfaces/implementations/elements/entity.py b/dbt_semantic_interfaces/implementations/elements/entity.py index ac98e4db..7c647ac1 100644 --- a/dbt_semantic_interfaces/implementations/elements/entity.py +++ b/dbt_semantic_interfaces/implementations/elements/entity.py @@ -1,16 +1,31 @@ from __future__ import annotations -from typing import Optional +from typing import Any, Dict, Optional + +from pydantic import Field +from typing_extensions import override from dbt_semantic_interfaces.implementations.base import ( HashableBaseModel, ModelWithMetadataParsing, ) from dbt_semantic_interfaces.implementations.metadata import PydanticMetadata +from dbt_semantic_interfaces.protocols.entity import EntityConfig +from dbt_semantic_interfaces.protocols.protocol_hint import ProtocolHint from dbt_semantic_interfaces.references import EntityReference from dbt_semantic_interfaces.type_enums import EntityType +class PydanticEntityConfig(HashableBaseModel, ProtocolHint[EntityConfig]): + """PydanticEntity config.""" + + @override + def _implements_protocol(self) -> EntityConfig: # noqa: D + return self + + meta: Dict[str, Any] = Field(default_factory=dict) + + class PydanticEntity(HashableBaseModel, ModelWithMetadataParsing): """Describes a entity.""" @@ -21,6 +36,7 @@ class PydanticEntity(HashableBaseModel, ModelWithMetadataParsing): expr: Optional[str] = None metadata: Optional[PydanticMetadata] = None label: Optional[str] = None + config: Optional[PydanticEntityConfig] @property def reference(self) -> EntityReference: # noqa: D diff --git a/dbt_semantic_interfaces/implementations/elements/measure.py b/dbt_semantic_interfaces/implementations/elements/measure.py index 5da1da4f..c98bb559 100644 --- a/dbt_semantic_interfaces/implementations/elements/measure.py +++ b/dbt_semantic_interfaces/implementations/elements/measure.py @@ -1,12 +1,16 @@ from __future__ import annotations -from typing import List, Optional +from typing import Any, Dict, List, Optional + +from typing_extensions import override from dbt_semantic_interfaces.implementations.base import ( HashableBaseModel, ModelWithMetadataParsing, ) from dbt_semantic_interfaces.implementations.metadata import PydanticMetadata +from dbt_semantic_interfaces.protocols.measure import MeasureConfig +from dbt_semantic_interfaces.protocols.protocol_hint import ProtocolHint from dbt_semantic_interfaces.references import MeasureReference from dbt_semantic_interfaces.type_enums import AggregationType from dsi_pydantic_shim import Field @@ -33,6 +37,16 @@ class PydanticMeasureAggregationParameters(HashableBaseModel): use_approximate_percentile: bool = False +class PydanticMeasureConfig(HashableBaseModel, ProtocolHint[MeasureConfig]): + """PydanticMeasure config.""" + + @override + def _implements_protocol(self) -> MeasureConfig: # noqa: D + return self + + meta: Dict[str, Any] = Field(default_factory=dict) + + class PydanticMeasure(HashableBaseModel, ModelWithMetadataParsing): """Describes a measure.""" @@ -46,6 +60,7 @@ class PydanticMeasure(HashableBaseModel, ModelWithMetadataParsing): non_additive_dimension: Optional[PydanticNonAdditiveDimensionParameters] = None agg_time_dimension: Optional[str] = None label: Optional[str] = None + config: Optional[PydanticMeasureConfig] = None @property def reference(self) -> MeasureReference: # noqa: D diff --git a/dbt_semantic_interfaces/protocols/dimension.py b/dbt_semantic_interfaces/protocols/dimension.py index 5cf8cbfe..5af912f7 100644 --- a/dbt_semantic_interfaces/protocols/dimension.py +++ b/dbt_semantic_interfaces/protocols/dimension.py @@ -1,7 +1,7 @@ from __future__ import annotations from abc import abstractmethod -from typing import Optional, Protocol +from typing import Any, Dict, Optional, Protocol from dbt_semantic_interfaces.protocols.metadata import Metadata from dbt_semantic_interfaces.references import ( @@ -46,6 +46,16 @@ def validity_params(self) -> Optional[DimensionValidityParams]: # noqa: D pass +class DimensionConfig(Protocol): # noqa: D + """The config property allows you to configure additional resources/metadata.""" + + @property + @abstractmethod + def meta(self) -> Dict[str, Any]: + """The meta field can be used to set metadata for a resource.""" + pass + + class Dimension(Protocol): """Describes a dimension.""" @@ -107,3 +117,8 @@ def validity_params(self) -> Optional[DimensionValidityParams]: def label(self) -> Optional[str]: """Returns a string representing a human readable label for the dimension.""" pass + + @property + @abstractmethod + def config(self) -> Optional[DimensionConfig]: # noqa: D + pass diff --git a/dbt_semantic_interfaces/protocols/entity.py b/dbt_semantic_interfaces/protocols/entity.py index 356e6803..591da359 100644 --- a/dbt_semantic_interfaces/protocols/entity.py +++ b/dbt_semantic_interfaces/protocols/entity.py @@ -1,12 +1,22 @@ from __future__ import annotations from abc import abstractmethod -from typing import Optional, Protocol +from typing import Any, Dict, Optional, Protocol from dbt_semantic_interfaces.references import EntityReference from dbt_semantic_interfaces.type_enums import EntityType +class EntityConfig(Protocol): # noqa: D + """The config property allows you to configure additional resources/metadata.""" + + @property + @abstractmethod + def meta(self) -> Dict[str, Any]: + """The meta field can be used to set metadata for a resource.""" + pass + + class Entity(Protocol): """Describes a entity.""" @@ -60,3 +70,8 @@ def is_linkable_entity_type(self) -> bool: def label(self) -> Optional[str]: """Returns a string representing a human readable label for the entity.""" pass + + @property + @abstractmethod + def config(self) -> Optional[EntityConfig]: # noqa: D + pass diff --git a/dbt_semantic_interfaces/protocols/measure.py b/dbt_semantic_interfaces/protocols/measure.py index a7588cfb..05075dab 100644 --- a/dbt_semantic_interfaces/protocols/measure.py +++ b/dbt_semantic_interfaces/protocols/measure.py @@ -1,7 +1,7 @@ from __future__ import annotations from abc import abstractmethod -from typing import Optional, Protocol, Sequence +from typing import Any, Dict, Optional, Protocol, Sequence from dbt_semantic_interfaces.references import MeasureReference from dbt_semantic_interfaces.type_enums import AggregationType @@ -45,6 +45,16 @@ def use_approximate_percentile(self) -> bool: # noqa: D pass +class MeasureConfig(Protocol): # noqa: D + """The config property allows you to configure additional resources/metadata.""" + + @property + @abstractmethod + def meta(self) -> Dict[str, Any]: + """The meta field can be used to set metadata for a resource.""" + pass + + class Measure(Protocol): """Describes a measure. @@ -98,3 +108,8 @@ def reference(self) -> MeasureReference: def label(self) -> Optional[str]: """Returns a string representing a human readable label for the measure.""" pass + + @property + @abstractmethod + def config(self) -> Optional[MeasureConfig]: # noqa: D + pass diff --git a/tests/parsing/test_semantic_model_parsing.py b/tests/parsing/test_semantic_model_parsing.py index 8faec41f..1ab89853 100644 --- a/tests/parsing/test_semantic_model_parsing.py +++ b/tests/parsing/test_semantic_model_parsing.py @@ -110,7 +110,9 @@ def test_base_semantic_model_entity_parsing() -> None: role: test_role expr: example_id label: {label} - + config: + meta: + random: metadata """ ) file = YamlConfigFile(filepath="test_dir/inline_for_test", contents=yaml_contents) @@ -206,7 +208,9 @@ def test_base_semantic_model_measure_parsing() -> None: expr: example_input description: {description} label: {label} - + config: + meta: + random: metadata """ ) file = YamlConfigFile(filepath="test_dir/inline_for_test", contents=yaml_contents) @@ -325,6 +329,9 @@ def test_semantic_model_categorical_dimension_parsing() -> None: - name: example_categorical_dimension type: categorical expr: dimension_input + config: + meta: + random: metadata """ ) file = YamlConfigFile(filepath="inline_for_test", contents=yaml_contents) diff --git a/tests/test_implements_satisfy_protocols.py b/tests/test_implements_satisfy_protocols.py index b37b5ca7..87771816 100644 --- a/tests/test_implements_satisfy_protocols.py +++ b/tests/test_implements_satisfy_protocols.py @@ -5,13 +5,18 @@ from dbt_semantic_interfaces.implementations.elements.dimension import ( PydanticDimension, + PydanticDimensionConfig, PydanticDimensionTypeParams, PydanticDimensionValidityParams, ) -from dbt_semantic_interfaces.implementations.elements.entity import PydanticEntity +from dbt_semantic_interfaces.implementations.elements.entity import ( + PydanticEntity, + PydanticEntityConfig, +) from dbt_semantic_interfaces.implementations.elements.measure import ( PydanticMeasure, PydanticMeasureAggregationParameters, + PydanticMeasureConfig, PydanticNonAdditiveDimensionParameters, ) from dbt_semantic_interfaces.implementations.export import PydanticExport @@ -66,6 +71,7 @@ expr=OPTIONAL_STR_STRATEGY, metadata=OPTIONAL_METADATA_STRATEGY, label=OPTIONAL_STR_STRATEGY, + config=builds(PydanticDimensionConfig), ) DIMENSION_VALIDITY_PARAMS_STRATEGY = builds( @@ -82,6 +88,7 @@ expr=OPTIONAL_STR_STRATEGY, metadata=OPTIONAL_METADATA_STRATEGY, label=OPTIONAL_STR_STRATEGY, + config=builds(PydanticDimensionConfig), ) DIMENSION_STRATEGY = TIME_DIMENSION_STRATEGY | CATEGORICAL_DIMENSION_STRATEGY @@ -93,6 +100,7 @@ expr=OPTIONAL_STR_STRATEGY, metadata=OPTIONAL_METADATA_STRATEGY, label=OPTIONAL_STR_STRATEGY, + config=builds(PydanticEntityConfig), ) MEASURE_STRATEGY = builds( @@ -104,6 +112,7 @@ non_additive_dimesnion=builds(PydanticNonAdditiveDimensionParameters) | none(), agg_time_dimension=OPTIONAL_STR_STRATEGY, label=OPTIONAL_STR_STRATEGY, + config=builds(PydanticMeasureConfig), ) SEMANTIC_MODEL_STRATEGY = builds(