diff --git a/.changes/unreleased/Features-20251217-140444.yaml b/.changes/unreleased/Features-20251217-140444.yaml new file mode 100644 index 0000000..af4e735 --- /dev/null +++ b/.changes/unreleased/Features-20251217-140444.yaml @@ -0,0 +1,3 @@ +kind: Features +body: Adding EnvironmentInfo query +time: 2025-12-17T14:04:44.035438-05:00 diff --git a/dbtsl/api/graphql/client/asyncio.pyi b/dbtsl/api/graphql/client/asyncio.pyi index af997c4..e9b2f2d 100644 --- a/dbtsl/api/graphql/client/asyncio.pyi +++ b/dbtsl/api/graphql/client/asyncio.pyi @@ -11,6 +11,7 @@ from dbtsl.models import ( AsyncMetric, Dimension, Entity, + EnvironmentInfo, Measure, SavedQuery, ) @@ -49,6 +50,9 @@ class AsyncGraphQLClient: async def saved_queries(self) -> List[SavedQuery]: """Get a list of all available saved queries.""" ... + async def environment_info(self) -> EnvironmentInfo: + """Get information about the Semantic Layer environment.""" + ... @overload async def compile_sql( diff --git a/dbtsl/api/graphql/client/sync.pyi b/dbtsl/api/graphql/client/sync.pyi index 5a1f365..25a886f 100644 --- a/dbtsl/api/graphql/client/sync.pyi +++ b/dbtsl/api/graphql/client/sync.pyi @@ -10,6 +10,7 @@ from dbtsl.api.shared.query_params import GroupByParam, OrderByGroupBy, OrderByM from dbtsl.models import ( Dimension, Entity, + EnvironmentInfo, Measure, SavedQuery, SyncMetric, @@ -81,7 +82,9 @@ class SyncGraphQLClient: def compile_sql(self, **query_params: Unpack[QueryParameters]) -> str: """Get the compiled SQL that would be sent to the warehouse by a query.""" ... - + def environment_info(self) -> EnvironmentInfo: + """Get information about the Semantic Layer environment.""" + ... @overload def query( self, diff --git a/dbtsl/api/graphql/protocol.py b/dbtsl/api/graphql/protocol.py index 3655412..86844a0 100644 --- a/dbtsl/api/graphql/protocol.py +++ b/dbtsl/api/graphql/protocol.py @@ -12,6 +12,7 @@ validate_query_parameters, ) from dbtsl.models import Dimension, Entity, Measure, Metric +from dbtsl.models.environment import EnvironmentInfo from dbtsl.models.query import QueryId, QueryResult, QueryStatus from dbtsl.models.saved_query import SavedQuery @@ -358,6 +359,29 @@ def parse_response(self, data: Dict[str, Any]) -> str: return cast(str, data["compileSql"]["sql"]) +class GetEnvironmentInfoOperation(ProtocolOperation[EmptyVariables, EnvironmentInfo]): + """Get information about the Semantic Layer environment.""" + + @override + def get_request_text(self, *, lazy: bool) -> str: + query = """ + query getEnvironmentInfo($environmentId: BigInt!) { + environmentInfo(environmentId: $environmentId) { + ...&fragment + } + } + """ + return render_query(query, EnvironmentInfo.gql_fragments(lazy=lazy)) + + @override + def get_request_variables(self, environment_id: int, variables: EmptyVariables) -> Dict[str, Any]: + return {"environmentId": environment_id} + + @override + def parse_response(self, data: Dict[str, Any]) -> EnvironmentInfo: + return decode_to_dataclass(data["environmentInfo"], EnvironmentInfo) + + class GraphQLProtocol: """Holds the GraphQL implementation for each of method in the API. @@ -373,3 +397,4 @@ class GraphQLProtocol: create_query = CreateQueryOperation() get_query_result = GetQueryResultOperation() compile_sql = CompileSqlOperation() + environment_info = GetEnvironmentInfoOperation() diff --git a/dbtsl/client/asyncio.pyi b/dbtsl/client/asyncio.pyi index cece74c..f8d8064 100644 --- a/dbtsl/client/asyncio.pyi +++ b/dbtsl/client/asyncio.pyi @@ -7,7 +7,7 @@ import pyarrow as pa from typing_extensions import Self, Unpack, overload from dbtsl.api.shared.query_params import GroupByParam, OrderByGroupBy, OrderByMetric, QueryParameters -from dbtsl.models import AsyncMetric, Dimension, Entity, Measure, SavedQuery +from dbtsl.models import AsyncMetric, Dimension, Entity, EnvironmentInfo, Measure, SavedQuery from dbtsl.timeout import TimeoutOptions class AsyncSemanticLayerClient: @@ -116,6 +116,10 @@ class AsyncSemanticLayerClient: """Get a list of all available saved queries.""" ... + async def environment_info(self) -> EnvironmentInfo: + """Get information about the Semantic Layer environment.""" + ... + def session(self) -> AbstractAsyncContextManager[AsyncIterator[Self]]: """Establish a connection with the dbt Semantic Layer's servers.""" ... diff --git a/dbtsl/client/base.py b/dbtsl/client/base.py index 595ad1a..21d2fe6 100644 --- a/dbtsl/client/base.py +++ b/dbtsl/client/base.py @@ -24,6 +24,7 @@ class BaseSemanticLayerClient(ABC, Generic[TGQLClient, TADBCClient]): _METHOD_MAP = { "compile_sql": GRAPHQL, + "environment_info": GRAPHQL, "dimension_values": ADBC, "dimensions": GRAPHQL, "entities": GRAPHQL, diff --git a/dbtsl/client/sync.pyi b/dbtsl/client/sync.pyi index 2137434..b4a5b1a 100644 --- a/dbtsl/client/sync.pyi +++ b/dbtsl/client/sync.pyi @@ -7,7 +7,7 @@ import pyarrow as pa from typing_extensions import Self, Unpack, overload from dbtsl.api.shared.query_params import GroupByParam, OrderByGroupBy, OrderByMetric, QueryParameters -from dbtsl.models import Dimension, Entity, Measure, SavedQuery, SyncMetric +from dbtsl.models import Dimension, Entity, EnvironmentInfo, Measure, SavedQuery, SyncMetric from dbtsl.timeout import TimeoutOptions class SyncSemanticLayerClient: @@ -115,6 +115,10 @@ class SyncSemanticLayerClient: """Get a list of all available saved queries.""" ... + def environment_info(self) -> EnvironmentInfo: + """Get information about the Semantic Layer environment.""" + ... + def session(self) -> AbstractContextManager[Iterator[Self]]: """Establish a connection with the dbt Semantic Layer's servers.""" ... diff --git a/dbtsl/models/__init__.py b/dbtsl/models/__init__.py index 6a97e82..0e0c56f 100644 --- a/dbtsl/models/__init__.py +++ b/dbtsl/models/__init__.py @@ -7,6 +7,7 @@ from .base import BaseModel, GraphQLFragmentMixin from .dimension import Dimension, DimensionType from .entity import Entity, EntityType +from .environment import EnvironmentInfo, SqlDialect, SqlEngine from .measure import AggregationType, Measure from .metric import AsyncMetric, Metric, MetricType, SyncMetric from .query import QueryResult @@ -36,6 +37,7 @@ "DimensionType", "Entity", "EntityType", + "EnvironmentInfo", "Export", "ExportConfig", "ExportDestinationType", @@ -48,6 +50,8 @@ "SavedQueryMetricParam", "SavedQueryQueryParams", "SavedQueryWhereParam", + "SqlDialect", + "SqlEngine", "SyncMetric", "TimeGranularity", ] diff --git a/dbtsl/models/environment.py b/dbtsl/models/environment.py new file mode 100644 index 0000000..1cdabf7 --- /dev/null +++ b/dbtsl/models/environment.py @@ -0,0 +1,45 @@ +from dataclasses import dataclass +from enum import Enum + +from dbtsl.models.base import BaseModel, FlexibleEnumMeta, GraphQLFragmentMixin + + +class SqlDialect(Enum, metaclass=FlexibleEnumMeta): + """The SQL dialect of the semantic layer.""" + + UNKNOWN = "UNKNOWN" + SNOWFLAKE = "SNOWFLAKE" + BIGQUERY = "BIGQUERY" + POSTGRES = "POSTGRES" + REDSHIFT = "REDSHIFT" + DATABRICKS = "DATABRICKS" + APACHE_SPARK = "APACHE_SPARK" + DATABRICKS_SPARK = "DATABRICKS_SPARK" + TRINO = "TRINO" + ATHENA = "ATHENA" + FABRIC = "FABRIC" + SYNAPSE = "SYNAPSE" + TERADATA = "TERADATA" + + +class SqlEngine(Enum, metaclass=FlexibleEnumMeta): + """The SQL engine/warehouse type.""" + + UNKNOWN = "UNKNOWN" + BIGQUERY = "BIGQUERY" + DUCKDB = "DUCKDB" + REDSHIFT = "REDSHIFT" + POSTGRES = "POSTGRES" + SNOWFLAKE = "SNOWFLAKE" + DATABRICKS = "DATABRICKS" + TRINO = "TRINO" + + +@dataclass +class EnvironmentInfo(BaseModel, GraphQLFragmentMixin): + """Information about the dbt Semantic Layer environment.""" + + sql_dialect: SqlDialect + has_metrics_defined: bool + dialect: SqlEngine + dialect_supported_by_slg: bool diff --git a/tests/api/graphql/test_protocol.py b/tests/api/graphql/test_protocol.py index 4f4e199..d3ac176 100644 --- a/tests/api/graphql/test_protocol.py +++ b/tests/api/graphql/test_protocol.py @@ -16,6 +16,7 @@ "get_query_result": [{"query_id": 1}], "create_query": TEST_QUERIES, "compile_sql": TEST_QUERIES, + "environment_info": [{}], } TestCase = Tuple[str, Dict[str, Any]]