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 meta to measures, entities, and dimensions #358

Merged
merged 5 commits into from
Nov 12, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20241023-113450.yaml
Original file line number Diff line number Diff line change
@@ -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
18 changes: 18 additions & 0 deletions dbt_semantic_interfaces/implementations/element_config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
from typing import Any, Dict

from pydantic import Field
from typing_extensions import override

from dbt_semantic_interfaces.implementations.base import HashableBaseModel
from dbt_semantic_interfaces.protocols.meta import SemanticLayerElementConfig
from dbt_semantic_interfaces.protocols.protocol_hint import ProtocolHint


class PydanticSemanticLayerElementConfig(HashableBaseModel, ProtocolHint[SemanticLayerElementConfig]):
"""PydanticDimension config."""

@override
def _implements_protocol(self) -> SemanticLayerElementConfig: # noqa: D
return self

meta: Dict[str, Any] = Field(default_factory=dict)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this work if we add more fields to the config and wanted to treat meta as optional? (For example, if we wanted to allow someone to add either some metadata or a tag.)

(This is not necessarily a blocking question.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! We can make this optional in the future, though, without a breaking change.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this not already optional? In a sense that, it'll default to an empty dict if you didn't provide it anything

Copy link
Contributor Author

@DevonFulcher DevonFulcher Nov 6, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ya, it is effectively optional because of the default, although not type Optional.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
HashableBaseModel,
ModelWithMetadataParsing,
)
from dbt_semantic_interfaces.implementations.element_config import (
PydanticSemanticLayerElementConfig,
)
from dbt_semantic_interfaces.implementations.metadata import PydanticMetadata
from dbt_semantic_interfaces.references import (
DimensionReference,
Expand Down Expand Up @@ -48,6 +51,7 @@ class PydanticDimension(HashableBaseModel, ModelWithMetadataParsing):
expr: Optional[str] = None
metadata: Optional[PydanticMetadata]
label: Optional[str] = None
config: Optional[PydanticSemanticLayerElementConfig]

@property
def reference(self) -> DimensionReference: # noqa: D
Expand Down
4 changes: 4 additions & 0 deletions dbt_semantic_interfaces/implementations/elements/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
HashableBaseModel,
ModelWithMetadataParsing,
)
from dbt_semantic_interfaces.implementations.element_config import (
PydanticSemanticLayerElementConfig,
)
from dbt_semantic_interfaces.implementations.metadata import PydanticMetadata
from dbt_semantic_interfaces.references import EntityReference
from dbt_semantic_interfaces.type_enums import EntityType
Expand All @@ -21,6 +24,7 @@ class PydanticEntity(HashableBaseModel, ModelWithMetadataParsing):
expr: Optional[str] = None
metadata: Optional[PydanticMetadata] = None
label: Optional[str] = None
config: Optional[PydanticSemanticLayerElementConfig]

@property
def reference(self) -> EntityReference: # noqa: D
Expand Down
4 changes: 4 additions & 0 deletions dbt_semantic_interfaces/implementations/elements/measure.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
HashableBaseModel,
ModelWithMetadataParsing,
)
from dbt_semantic_interfaces.implementations.element_config import (
PydanticSemanticLayerElementConfig,
)
from dbt_semantic_interfaces.implementations.metadata import PydanticMetadata
from dbt_semantic_interfaces.references import MeasureReference
from dbt_semantic_interfaces.type_enums import AggregationType
Expand Down Expand Up @@ -46,6 +49,7 @@ class PydanticMeasure(HashableBaseModel, ModelWithMetadataParsing):
non_additive_dimension: Optional[PydanticNonAdditiveDimensionParameters] = None
agg_time_dimension: Optional[str] = None
label: Optional[str] = None
config: Optional[PydanticSemanticLayerElementConfig] = None

@property
def reference(self) -> MeasureReference: # noqa: D
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,19 @@
],
"type": "object"
},
"dimension_config_schema": {
"$id": "dimension_config_schema",
"additionalProperties": false,
"properties": {
"meta": {
"propertyNames": {
"type": "string"
},
"type": "object"
}
},
"type": "object"
},
"dimension_schema": {
"$id": "dimension_schema",
"additionalProperties": false,
Expand All @@ -151,6 +164,9 @@
}
],
"properties": {
"config": {
"$ref": "#/definitions/dimension_config_schema"
},
"description": {
"type": "string"
},
Expand Down Expand Up @@ -227,10 +243,26 @@
],
"type": "object"
},
"entity_config_schema": {
"$id": "entity_config_schema",
"additionalProperties": false,
"properties": {
"meta": {
"propertyNames": {
"type": "string"
},
"type": "object"
}
},
"type": "object"
},
"entity_schema": {
"$id": "entity_schema",
"additionalProperties": false,
"properties": {
"config": {
"$ref": "#/definitions/entity_config_schema"
},
"entity": {
"type": "string"
},
Expand Down Expand Up @@ -337,6 +369,19 @@
"type"
]
},
"measure_config_schema": {
"$id": "measure_config_schema",
"additionalProperties": false,
"properties": {
"meta": {
"propertyNames": {
"type": "string"
},
"type": "object"
}
},
"type": "object"
},
"measure_schema": {
"$id": "measure_schema",
"additionalProperties": false,
Expand Down Expand Up @@ -370,6 +415,9 @@
"pattern": "(?!.*__).*^[a-z][a-z0-9_]*[a-z0-9]$",
"type": "string"
},
"config": {
"$ref": "#/definitions/measure_config_schema"
},
"create_metric": {
"type": "boolean"
},
Expand Down
34 changes: 34 additions & 0 deletions dbt_semantic_interfaces/parsing/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,16 @@
"additionalProperties": False,
}


entity_config_schema = {
"$id": "entity_config_schema",
"type": "object",
"properties": {
"meta": {"type": "object", "propertyNames": {"type": "string"}},
},
"additionalProperties": False,
}

entity_schema = {
"$id": "entity_schema",
"type": "object",
Expand All @@ -174,6 +184,7 @@
"expr": {"type": ["string", "boolean"]},
"entity": {"type": "string"},
"label": {"type": "string"},
"config": {"$ref": "entity_config_schema"},
},
"additionalProperties": False,
"required": ["name", "type"],
Expand Down Expand Up @@ -226,6 +237,15 @@
"additionalProperties": False,
}

measure_config_schema = {
"$id": "measure_config_schema",
"type": "object",
"properties": {
"meta": {"type": "object", "propertyNames": {"type": "string"}},
},
"additionalProperties": False,
}

measure_schema = {
"$id": "measure_schema",
"type": "object",
Expand All @@ -248,11 +268,21 @@
},
"description": {"type": "string"},
"label": {"type": "string"},
"config": {"$ref": "measure_config_schema"},
},
"additionalProperties": False,
"required": ["name", "agg"],
}

dimension_config_schema = {
"$id": "dimension_config_schema",
"type": "object",
"properties": {
"meta": {"type": "object", "propertyNames": {"type": "string"}},
},
"additionalProperties": False,
}

dimension_schema = {
"$id": "dimension_schema",
"type": "object",
Expand All @@ -267,6 +297,7 @@
"expr": {"type": ["string", "boolean"]},
"type_params": {"$ref": "dimension_type_params_schema"},
"label": {"type": "string"},
"config": {"$ref": "dimension_config_schema"},
},
# dimension must have type_params if its a time dimension
"anyOf": [{"not": {"$ref": "#/definitions/is-time-dimension"}}, {"required": ["type_params"]}],
Expand Down Expand Up @@ -529,6 +560,9 @@
saved_query_query_params_schema["$id"]: saved_query_query_params_schema,
semantic_model_config_schema["$id"]: semantic_model_config_schema,
metric_config_schema["$id"]: metric_config_schema,
dimension_config_schema["$id"]: dimension_config_schema,
entity_config_schema["$id"]: entity_config_schema,
measure_config_schema["$id"]: measure_config_schema,
}

resources: List[Tuple[str, Resource]] = [(str(k), DRAFT7.create_resource(v)) for k, v in schema_store.items()]
Expand Down
6 changes: 6 additions & 0 deletions dbt_semantic_interfaces/protocols/dimension.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from abc import abstractmethod
from typing import Optional, Protocol

from dbt_semantic_interfaces.protocols.meta import SemanticLayerElementConfig
from dbt_semantic_interfaces.protocols.metadata import Metadata
from dbt_semantic_interfaces.references import (
DimensionReference,
Expand Down Expand Up @@ -107,3 +108,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[SemanticLayerElementConfig]: # noqa: D
pass
6 changes: 6 additions & 0 deletions dbt_semantic_interfaces/protocols/entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from abc import abstractmethod
from typing import Optional, Protocol

from dbt_semantic_interfaces.protocols.meta import SemanticLayerElementConfig
from dbt_semantic_interfaces.references import EntityReference
from dbt_semantic_interfaces.type_enums import EntityType

Expand Down Expand Up @@ -60,3 +61,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[SemanticLayerElementConfig]: # noqa: D
pass
6 changes: 6 additions & 0 deletions dbt_semantic_interfaces/protocols/measure.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
from abc import abstractmethod
from typing import Optional, Protocol, Sequence

from dbt_semantic_interfaces.protocols.meta import SemanticLayerElementConfig
from dbt_semantic_interfaces.references import MeasureReference
from dbt_semantic_interfaces.type_enums import AggregationType

Expand Down Expand Up @@ -98,3 +99,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[SemanticLayerElementConfig]: # noqa: D
pass
12 changes: 12 additions & 0 deletions dbt_semantic_interfaces/protocols/meta.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from abc import abstractmethod
from typing import Any, Dict, Protocol


class SemanticLayerElementConfig(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
11 changes: 9 additions & 2 deletions tests/parsing/test_semantic_model_parsing.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
7 changes: 7 additions & 0 deletions tests/test_implements_satisfy_protocols.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
from hypothesis import given
from hypothesis.strategies import booleans, builds, from_type, just, lists, none, text

from dbt_semantic_interfaces.implementations.element_config import (
PydanticSemanticLayerElementConfig,
)
from dbt_semantic_interfaces.implementations.elements.dimension import (
PydanticDimension,
PydanticDimensionTypeParams,
Expand Down Expand Up @@ -66,6 +69,7 @@
expr=OPTIONAL_STR_STRATEGY,
metadata=OPTIONAL_METADATA_STRATEGY,
label=OPTIONAL_STR_STRATEGY,
config=builds(PydanticSemanticLayerElementConfig),
)

DIMENSION_VALIDITY_PARAMS_STRATEGY = builds(
Expand All @@ -82,6 +86,7 @@
expr=OPTIONAL_STR_STRATEGY,
metadata=OPTIONAL_METADATA_STRATEGY,
label=OPTIONAL_STR_STRATEGY,
config=builds(PydanticSemanticLayerElementConfig),
)

DIMENSION_STRATEGY = TIME_DIMENSION_STRATEGY | CATEGORICAL_DIMENSION_STRATEGY
Expand All @@ -93,6 +98,7 @@
expr=OPTIONAL_STR_STRATEGY,
metadata=OPTIONAL_METADATA_STRATEGY,
label=OPTIONAL_STR_STRATEGY,
config=builds(PydanticSemanticLayerElementConfig),
)

MEASURE_STRATEGY = builds(
Expand All @@ -104,6 +110,7 @@
non_additive_dimesnion=builds(PydanticNonAdditiveDimensionParameters) | none(),
agg_time_dimension=OPTIONAL_STR_STRATEGY,
label=OPTIONAL_STR_STRATEGY,
config=builds(PydanticSemanticLayerElementConfig),
)

SEMANTIC_MODEL_STRATEGY = builds(
Expand Down
Loading