Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,8 @@ def openapi_types():
'sort_direction': (str,), # noqa: E501
'source_column_data_type': (str,), # noqa: E501
'tags': ([str],), # noqa: E501
'is_nullable': (bool,), # noqa: E501
'null_value': (str,), # noqa: E501
}

@cached_property
Expand All @@ -157,6 +159,8 @@ def discriminator():
'sort_direction': 'sortDirection', # noqa: E501
'source_column_data_type': 'sourceColumnDataType', # noqa: E501
'tags': 'tags', # noqa: E501
'is_nullable': 'isNullable', # noqa: E501
'null_value': 'nullValue', # noqa: E501
}

read_only_vars = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,8 @@ def openapi_types():
'is_primary_key': (bool,), # noqa: E501
'referenced_table_column': (str,), # noqa: E501
'referenced_table_id': (str,), # noqa: E501
'is_nullable': (bool,), # noqa: E501
'null_value': (str,), # noqa: E501
}

@cached_property
Expand All @@ -123,6 +125,8 @@ def discriminator():
'is_primary_key': 'isPrimaryKey', # noqa: E501
'referenced_table_column': 'referencedTableColumn', # noqa: E501
'referenced_table_id': 'referencedTableId', # noqa: E501
'is_nullable': 'isNullable', # noqa: E501
'null_value': 'nullValue', # noqa: E501
}

read_only_vars = {
Expand Down Expand Up @@ -174,6 +178,8 @@ def _from_openapi_data(cls, data_type, name, *args, **kwargs): # noqa: E501
is_primary_key (bool): Is column part of primary key?. [optional] # noqa: E501
referenced_table_column (str): Referenced table (Foreign key). [optional] # noqa: E501
referenced_table_id (str): Referenced table (Foreign key). [optional] # noqa: E501
is_nullable (bool): Is column nullable?. [optional] # noqa: E501
null_value (str): Value to use for null values. [optional] # noqa: E501
"""

_check_type = kwargs.pop('_check_type', True)
Expand Down Expand Up @@ -269,6 +275,8 @@ def __init__(self, data_type, name, *args, **kwargs): # noqa: E501
is_primary_key (bool): Is column part of primary key?. [optional] # noqa: E501
referenced_table_column (str): Referenced table (Foreign key). [optional] # noqa: E501
referenced_table_id (str): Referenced table (Foreign key). [optional] # noqa: E501
is_nullable (bool): Is column nullable?. [optional] # noqa: E501
null_value (str): Value to use for null values. [optional] # noqa: E501
"""

_check_type = kwargs.pop('_check_type', True)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ def openapi_types():
'is_hidden': (bool,), # noqa: E501
'source_column_data_type': (str,), # noqa: E501
'tags': ([str],), # noqa: E501
'is_nullable': (bool,), # noqa: E501
'null_value': (str,), # noqa: E501
}

@cached_property
Expand All @@ -132,6 +134,8 @@ def discriminator():
'is_hidden': 'isHidden', # noqa: E501
'source_column_data_type': 'sourceColumnDataType', # noqa: E501
'tags': 'tags', # noqa: E501
'is_nullable': 'isNullable', # noqa: E501
'null_value': 'nullValue', # noqa: E501
}

read_only_vars = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ class DeclarativeSetting(ModelNormal):
'SORT_CASE_SENSITIVE': "SORT_CASE_SENSITIVE",
'METRIC_FORMAT_OVERRIDE': "METRIC_FORMAT_OVERRIDE",
'ENABLE_AI_ON_DATA': "ENABLE_AI_ON_DATA",
'ENABLE_NULL_JOINS': "ENABLE_NULL_JOINS",
},
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ class CatalogDeclarativeColumn(Base):
is_primary_key: Optional[bool] = None
referenced_table_id: Optional[str] = None
referenced_table_column: Optional[str] = None
is_nullable: Optional[bool] = None
null_value: Optional[str] = None

@staticmethod
def client_class() -> type[DeclarativeColumn]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ class CatalogDeclarativeAttribute(Base):
tags: Optional[list[str]] = None
is_hidden: Optional[bool] = None
locale: Optional[str] = None
is_nullable: Optional[bool] = None
null_value: Optional[str] = None

@staticmethod
def client_class() -> type[DeclarativeAttribute]:
Expand All @@ -94,6 +96,8 @@ class CatalogDeclarativeFact(Base):
description: Optional[str] = None
tags: Optional[list[str]] = None
is_hidden: Optional[bool] = None
is_nullable: Optional[bool] = None
null_value: Optional[str] = None

@staticmethod
def client_class() -> type[DeclarativeFact]:
Expand All @@ -118,6 +122,8 @@ class CatalogDeclarativeAggregatedFact(Base):
source_column_data_type: Optional[str] = None
description: Optional[str] = None
tags: Optional[list[str]] = None
is_nullable: Optional[bool] = None
null_value: Optional[str] = None

@staticmethod
def client_class() -> type[DeclarativeAggregatedFact]:
Expand Down Expand Up @@ -167,6 +173,8 @@ class CatalogDeclarativeLabel(Base):
is_hidden: Optional[bool] = None
locale: Optional[str] = None
translations: Optional[list[CatalogDeclarativeLabelTranslation]] = None
is_nullable: Optional[bool] = None
null_value: Optional[str] = None

@staticmethod
def client_class() -> type[DeclarativeLabel]:
Expand Down Expand Up @@ -201,6 +209,8 @@ class CatalogDeclarativeReferenceSource(Base):
column: str
target: CatalogGrainIdentifier
data_type: Optional[str] = None
is_nullable: Optional[bool] = None
null_value: Optional[str] = None

@staticmethod
def client_class() -> type[DeclarativeReferenceSource]:
Expand Down
36 changes: 36 additions & 0 deletions packages/gooddata-sdk/tests/catalog/test_null_joins_setting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# (C) 2024 GoodData Corporation
"""Tests for ENABLE_NULL_JOINS setting support."""

import pytest
from gooddata_sdk.catalog.setting import CatalogDeclarativeSetting


def test_enable_null_joins_setting():
"""Test that ENABLE_NULL_JOINS setting can be created."""
setting = CatalogDeclarativeSetting(
id="enable_null_joins_test",
type="ENABLE_NULL_JOINS",
content={"enabled": True}
)

assert setting.id == "enable_null_joins_test"
assert setting.type == "ENABLE_NULL_JOINS"
assert setting.content == {"enabled": True}

# Test conversion to API object
api_obj = setting.to_api()
assert api_obj is not None

# Test that the API object has the correct class
assert setting.client_class().__name__ == "DeclarativeSetting"


def test_enable_null_joins_setting_validation():
"""Test that the ENABLE_NULL_JOINS setting type is valid."""
# This should not raise a validation error
setting = CatalogDeclarativeSetting(
id="enable_null_joins_validation_test",
type="ENABLE_NULL_JOINS"
)

assert setting.type == "ENABLE_NULL_JOINS"
137 changes: 137 additions & 0 deletions packages/gooddata-sdk/tests/catalog/test_nullable_columns.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# (C) 2024 GoodData Corporation
"""Tests for nullable columns support in declarative models."""

import pytest
from gooddata_sdk.catalog.data_source.declarative_model.physical_model.column import CatalogDeclarativeColumn
from gooddata_sdk.catalog.workspace.declarative_model.workspace.logical_model.dataset.dataset import (
CatalogDeclarativeAttribute,
CatalogDeclarativeFact,
CatalogDeclarativeLabel,
CatalogDeclarativeAggregatedFact,
CatalogDeclarativeReferenceSource,
)
from gooddata_sdk.catalog.identifier import CatalogGrainIdentifier


def test_catalog_declarative_column_nullable_fields():
"""Test that CatalogDeclarativeColumn supports nullable fields."""
column = CatalogDeclarativeColumn(
name="test_column",
data_type="STRING",
is_nullable=True,
null_value="NULL"
)

assert column.name == "test_column"
assert column.data_type == "STRING"
assert column.is_nullable is True
assert column.null_value == "NULL"

# Test default values
column_default = CatalogDeclarativeColumn(
name="default_column",
data_type="INT"
)
assert column_default.is_nullable is None
assert column_default.null_value is None


def test_catalog_declarative_fact_nullable_fields():
"""Test that CatalogDeclarativeFact supports nullable fields."""
fact = CatalogDeclarativeFact(
id="test_fact",
title="Test Fact",
source_column="test_column",
is_nullable=True,
null_value="0"
)

assert fact.id == "test_fact"
assert fact.title == "Test Fact"
assert fact.source_column == "test_column"
assert fact.is_nullable is True
assert fact.null_value == "0"


def test_catalog_declarative_attribute_nullable_fields():
"""Test that CatalogDeclarativeAttribute supports nullable fields."""
attribute = CatalogDeclarativeAttribute(
id="test_attribute",
title="Test Attribute",
source_column="test_column",
labels=[],
is_nullable=True,
null_value="UNKNOWN"
)

assert attribute.id == "test_attribute"
assert attribute.title == "Test Attribute"
assert attribute.source_column == "test_column"
assert attribute.is_nullable is True
assert attribute.null_value == "UNKNOWN"


def test_catalog_declarative_label_nullable_fields():
"""Test that CatalogDeclarativeLabel supports nullable fields."""
label = CatalogDeclarativeLabel(
id="test_label",
title="Test Label",
source_column="test_column",
is_nullable=False,
null_value=None
)

assert label.id == "test_label"
assert label.title == "Test Label"
assert label.source_column == "test_column"
assert label.is_nullable is False
assert label.null_value is None


def test_catalog_declarative_aggregated_fact_nullable_fields():
"""Test that CatalogDeclarativeAggregatedFact supports nullable fields."""
agg_fact = CatalogDeclarativeAggregatedFact(
id="test_agg_fact",
source_column="test_column",
is_nullable=True,
null_value="0.0"
)

assert agg_fact.id == "test_agg_fact"
assert agg_fact.source_column == "test_column"
assert agg_fact.is_nullable is True
assert agg_fact.null_value == "0.0"


def test_catalog_declarative_reference_source_nullable_fields():
"""Test that CatalogDeclarativeReferenceSource supports nullable fields."""
target = CatalogGrainIdentifier(id="target_id", type="attribute")

ref_source = CatalogDeclarativeReferenceSource(
column="ref_column",
target=target,
is_nullable=True,
null_value="EMPTY"
)

assert ref_source.column == "ref_column"
assert ref_source.target == target
assert ref_source.is_nullable is True
assert ref_source.null_value == "EMPTY"


def test_api_conversion():
"""Test that the models can convert to API objects."""
column = CatalogDeclarativeColumn(
name="test_column",
data_type="STRING",
is_nullable=True,
null_value="NULL"
)

# This should not raise an error
api_obj = column.to_api()
assert api_obj is not None

# Test that the API object has the correct class
assert column.client_class().__name__ == "DeclarativeColumn"
Loading