Skip to content

Commit

Permalink
feat(taps,targets): Support the singer.decimal JSON Schema extension
Browse files Browse the repository at this point in the history
  • Loading branch information
edgarrmondragon committed Nov 30, 2024
1 parent de53f85 commit ff67a40
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 3 deletions.
33 changes: 30 additions & 3 deletions singer_sdk/connectors/sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,10 +124,21 @@ class SQLToJSONSchema:
.. versionchanged:: 0.43.0
Added the :meth:`singer_sdk.connectors.sql.SQLToJSONSchema.from_config` class
method.
.. versionchanged:: 0.43.0
Added support for the `use_singer_decimal` option.
"""

def __init__(self, *, use_singer_decimal: bool) -> None:
"""Initialize the SQL to JSON Schema converter.
Args:
use_singer_decimal: Whether to represent numbers as `string` with
the `singer.decimal` format instead of as `number`.
"""
self.use_singer_decimal = use_singer_decimal

@classmethod
def from_config(cls: type[SQLToJSONSchema], config: dict) -> SQLToJSONSchema: # noqa: ARG003
def from_config(cls: type[SQLToJSONSchema], config: dict) -> SQLToJSONSchema:
"""Create a new instance from a configuration dictionary.
Override this to instantiate this converter with values from the tap's
Expand All @@ -146,11 +157,13 @@ def from_config(cls, config):
Args:
config: The configuration dictionary.
use_singer_decimal: Whether to represent numbers as `string` with
the `singer.decimal` format instead of as `number`.
Returns:
A new instance of the class.
"""
return cls()
return cls(use_singer_decimal=config.get("use_singer_decimal", False))

@functools.singledispatchmethod
def to_jsonschema(self, column_type: sa.types.TypeEngine) -> dict: # noqa: ARG002, D102, PLR6301
Expand Down Expand Up @@ -193,12 +206,14 @@ def integer_to_jsonschema(self, column_type: sa.types.Integer) -> dict: # noqa:
return th.IntegerType.type_dict # type: ignore[no-any-return]

@to_jsonschema.register
def float_to_jsonschema(self, column_type: sa.types.Numeric) -> dict: # noqa: ARG002, PLR6301
def float_to_jsonschema(self, column_type: sa.types.Numeric) -> dict: # noqa: ARG002
"""Return a JSON Schema representation of a generic number type.
Args:
column_type (:column_type:`Numeric`): The column type.
"""
if self.use_singer_decimal:
return th.SingerDecimalType.type_dict # type: ignore[no-any-return]
return th.NumberType.type_dict # type: ignore[no-any-return]

@to_jsonschema.register
Expand Down Expand Up @@ -272,6 +287,7 @@ def __init__(self, *, max_varchar_length: int | None = None) -> None:
"hostname": lambda _: sa.types.VARCHAR(253), # RFC 1035
"ipv4": lambda _: sa.types.VARCHAR(15),
"ipv6": lambda _: sa.types.VARCHAR(45),
"singer.decimal": self._handle_singer_decimal,
}

self._fallback_type: type[sa.types.TypeEngine] = sa.types.VARCHAR
Expand Down Expand Up @@ -323,6 +339,17 @@ def _invoke_handler( # noqa: PLR6301
return handler() # type: ignore[no-any-return]
return handler(schema)

def _handle_singer_decimal(self, schema: dict) -> sa.types.TypeEngine: # noqa: PLR6301
"""Handle a singer.decimal format.
Args:
schema: The JSON Schema object.
Returns:
The appropriate SQLAlchemy type.
"""
return sa.types.DECIMAL(schema.get("precision"), schema.get("scale"))

Check warning on line 351 in singer_sdk/connectors/sql.py

View check run for this annotation

Codecov / codecov/patch

singer_sdk/connectors/sql.py#L351

Added line #L351 was not covered by tests

@property
def fallback_type(self) -> type[sa.types.TypeEngine]:
"""Return the fallback type.
Expand Down
6 changes: 6 additions & 0 deletions singer_sdk/typing.py
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,12 @@ class RegexType(StringType):
string_format = "regex"


class SingerDecimalType(StringType):
"""Decimal type."""

string_format = "singer.decimal"


class BooleanType(JSONTypeHelper[bool]):
"""Boolean type.
Expand Down
8 changes: 8 additions & 0 deletions tests/core/test_connector_sql.py
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,14 @@ def my_type_to_jsonschema(self, column_type) -> dict: # noqa: ARG002
assert m.to_jsonschema(sa.types.BOOLEAN()) == {"type": ["boolean"]}


def test_numeric_to_singer_decimal():
converter = SQLToJSONSchema(use_singer_decimal=True)
assert converter.to_jsonschema(sa.types.NUMERIC()) == {
"type": ["string"],
"format": "singer.decimal",
}


class TestJSONSchemaToSQL: # noqa: PLR0904
@pytest.fixture
def json_schema_to_sql(self) -> JSONSchemaToSQL:
Expand Down

0 comments on commit ff67a40

Please sign in to comment.