Skip to content

Commit b685479

Browse files
committed
fix(semantic-layer): omit null dimension values instead of converting to empty string
Null values are not valid filter values and would produce invalid where clauses if returned. Also adds a test covering null omission and corrects the changelog entry describing the list[str] fix.
1 parent 0f489f6 commit b685479

3 files changed

Lines changed: 35 additions & 3 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
kind: Bug Fix
2-
body: 'Fix get_dimension_values: widen values type to list[Any], add error handling, add limit >= 1 validation'
2+
body: 'Fix get_dimension_values: stringify dimension values to maintain list[str] type, omit nulls, add error handling, add limit >= 1 validation'
33
time: 2026-05-19T14:21:46.951552+02:00

src/dbt_mcp/semantic_layer/client.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -372,9 +372,9 @@ async def get_dimension_values(
372372
metrics=metrics or [],
373373
group_by=dimension,
374374
)
375+
# SDK doesn't support server-side limiting; truncation is applied client-side.
375376
raw: list[str] = [
376-
str(v) if v is not None else ""
377-
for v in raw_table.column(dimension).to_pylist()
377+
str(v) for v in raw_table.column(dimension).to_pylist() if v is not None
378378
]
379379
truncated = len(raw) > limit
380380
return DimensionValuesResponse(values=raw[:limit], truncated=truncated)

tests/unit/semantic_layer/test_client.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,3 +1027,35 @@ async def test_get_dimension_values_no_metrics_passes_empty_list(mock_client_pro
10271027
)
10281028
assert result.values == ["active", "inactive"]
10291029
assert result.truncated is False
1030+
1031+
1032+
@pytest.mark.asyncio
1033+
async def test_get_dimension_values_omits_nulls(mock_client_provider):
1034+
mock_sl_client = MagicMock()
1035+
session_ctx = MagicMock()
1036+
mock_sl_client.session.return_value = session_ctx
1037+
session_ctx.__enter__ = MagicMock(return_value=mock_sl_client)
1038+
session_ctx.__exit__ = MagicMock(return_value=False)
1039+
mock_sl_client.dimension_values.return_value = pa.table(
1040+
{"customer__country": pa.array(["US", None, "FR"], type=pa.string())}
1041+
)
1042+
mock_client_provider.get_client.return_value = mock_sl_client
1043+
1044+
token_p = MagicMock()
1045+
token_p.get_token.return_value = "tok"
1046+
headers_p = MagicMock()
1047+
headers_p.get_headers.return_value = {}
1048+
config = SemanticLayerConfig(
1049+
url="https://test-host/api/graphql",
1050+
host="test-host",
1051+
prod_environment_id=123,
1052+
token_provider=token_p,
1053+
headers_provider=headers_p,
1054+
)
1055+
fetcher = SemanticLayerFetcher(client_provider=mock_client_provider)
1056+
result = await fetcher.get_dimension_values(
1057+
config=config, dimension="customer__country", metrics=None, limit=100
1058+
)
1059+
1060+
assert result.values == ["US", "FR"]
1061+
assert result.truncated is False

0 commit comments

Comments
 (0)