diff --git a/src/cbspy/client.py b/src/cbspy/client.py index 1ddca28..d9d5b96 100644 --- a/src/cbspy/client.py +++ b/src/cbspy/client.py @@ -76,12 +76,13 @@ def get_metadata(self, table_id: str) -> TableMetadata: prop_rows = self._odata.get_json(table_id, "DataProperties") columns = [self._parse_column(p) for p in prop_rows] + # CBS sends explicit nulls for missing fields, so `or` rather than a .get() default. return TableMetadata( - id=info.get("Identifier", table_id), - title=info.get("Title", ""), - description=info.get("ShortDescription", ""), - period=info.get("Period", ""), - frequency=info.get("Frequency", ""), + id=info.get("Identifier") or table_id, + title=info.get("Title") or "", + description=info.get("ShortDescription") or "", + period=info.get("Period") or "", + frequency=info.get("Frequency") or "", properties=columns, ) @@ -173,9 +174,9 @@ def _build_filter( def _parse_column(prop: dict[str, Any]) -> Column: """Convert a raw DataProperties entry to a Column model.""" return Column( - id=prop.get("Key", ""), - name=prop.get("Title", ""), - unit=prop.get("Unit", ""), - datatype=prop.get("Datatype", prop.get("Type", "")), - description=prop.get("Description", ""), + id=prop.get("Key") or "", + name=prop.get("Title") or "", + unit=prop.get("Unit") or "", + datatype=prop.get("Datatype") or prop.get("Type") or "", + description=prop.get("Description") or "", ) diff --git a/tests/test_client.py b/tests/test_client.py index b4ca533..d3dc69a 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -139,6 +139,59 @@ def test_not_found_raises(self): with pytest.raises(TableNotFoundError): client.get_metadata("FAKE") + @respx.mock + def test_null_fields_become_empty_strings(self): + # CBS sends explicit nulls, e.g. 86103NED has properties with "Description": null. + respx.get(f"{BASE}/ODataFeed/OData/86103NED/TableInfos").mock( + return_value=httpx.Response( + 200, + json={ + "value": [ + { + "Title": "Aardgasbalans; aanbod en verbruik", + "ShortDescription": None, + "Identifier": "86103NED", + "Period": "2015 - augustus 2026", + "Frequency": None, + } + ] + }, + ) + ) + respx.get(f"{BASE}/ODataFeed/OData/86103NED/DataProperties").mock( + return_value=httpx.Response( + 200, + json={ + "value": [ + { + "ID": 0, + "Key": "Perioden", + "Title": "Perioden", + "Description": None, + "Type": "TimeDimension", + "Unit": None, + }, + { + "ID": 1, + "Key": "TotaalVerbruik_25", + "Title": "Totaal verbruik", + "Description": None, + "Type": "Topic", + "Datatype": "Long", + "Unit": "mln m3", + }, + ] + }, + ) + ) + client = Client() + meta = client.get_metadata("86103NED") + assert meta.description == "" + assert meta.frequency == "" + assert meta.properties[0].unit == "" + assert meta.properties[1].description == "" + assert meta.properties[1].unit == "mln m3" + class TestGetData: @respx.mock