diff --git a/amqp/adapter/cleverthis_service_adapter.py b/amqp/adapter/cleverthis_service_adapter.py index 588508b..3e4c66b 100644 --- a/amqp/adapter/cleverthis_service_adapter.py +++ b/amqp/adapter/cleverthis_service_adapter.py @@ -37,6 +37,7 @@ from amqp.adapter.logging_utils import ( logging_info, logging_warning, ) +from amqp.adapter.pydantic_serializer import python_type_to_json from amqp.adapter.type_descriptor import TypeDescriptor from amqp.config.amq_configuration import AMQConfiguration from amqp.model.model import AMQErrorMessage, AMQMessage, DataMessage, DataResponse @@ -692,12 +693,12 @@ class CleverThisServiceAdapter: args=args, api_method=api_method ) logging_info( - "INVOKE ENDPOINT: {%s}, ARGS: {%s}", + "INVOKE ENDPOINT: [%s], ARGS: {%s}", api_method.__name__, ", ".join(f"{k}={v}" for k, v in _verified_args.items()), ) _result: Any = await api_method(**_verified_args) - logging_info(f"ENDPOINT RESULT: {api_method.__name__}, RESULT: {str(_result)}") + logging_info(f"ENDPOINT RESULT: [{api_method.__name__}], RESULT: {str(_result)}") if isinstance(_result, FileResponse): _json_result, _content_type = ( @@ -713,7 +714,10 @@ class CleverThisServiceAdapter: _json_result = _result.json() _content_type = "application/json" else: - _json_result = _result + if isinstance(_result, str): + _json_result = _result + else: + _json_result = python_type_to_json(_result) _content_type = "application/json" _binary_result = ( diff --git a/amqp/adapter/pydantic_serializer.py b/amqp/adapter/pydantic_serializer.py index 27a61ae..61185f4 100644 --- a/amqp/adapter/pydantic_serializer.py +++ b/amqp/adapter/pydantic_serializer.py @@ -5,7 +5,7 @@ This module provides serialization and deserialization functions for Pydantic mo import importlib import json import uuid -from datetime import datetime +from datetime import date, datetime from types import UnionType from typing import Any, Dict, Union, get_args, get_origin from urllib.parse import parse_qs, urlparse @@ -57,6 +57,35 @@ def serialize_object(obj: BaseModel) -> str: return f"{module_name}.{class_name}:{dict_string}" +def python_type_to_json(data): + """ + Convert Python objects (dict, list, tuple, set, datetime, etc.) to a JSON string. + Handles common non-JSON-serializable types like datetime, date, and sets. + + Args: + data: Python object to be converted (dict, list, tuple, set, etc.) + + Returns: + str: JSON string representation of the input data + + Raises: + TypeError: If the object contains non-serializable types (e.g., custom classes) + """ + + def default_serializer(obj): + """Custom serializer for non-JSON-serializable objects.""" + if isinstance(obj, (datetime, date)): + return obj.isoformat() # Convert datetime/date to ISO string + elif isinstance(obj, set): + return list(obj) # Convert sets to lists + elif hasattr(obj, "__dict__"): + return obj.__dict__ # Attempt to serialize custom objects + else: + raise TypeError(f"Object of type {type(obj)} is not JSON serializable") + + return json.dumps(data, default=default_serializer, indent=0, ensure_ascii=False) + + def try_deserialize_object(data: str) -> BaseModel | str: """ Try deserialize object, if failed, return the input string as-is. diff --git a/amqp/adapter/type_descriptor.py b/amqp/adapter/type_descriptor.py index 0b65674..4fbad7c 100644 --- a/amqp/adapter/type_descriptor.py +++ b/amqp/adapter/type_descriptor.py @@ -120,8 +120,12 @@ def _instantiate( return bool(payload) # Handle typing module types - if type_string.startswith("typing."): - if type_string.startswith("typing.List") or type_string.startswith("typing.Sequence"): + if type_string.startswith("typing.") or type_string.startswith("list"): + if ( + type_string.startswith("typing.List") + or type_string.startswith("typing.Sequence") + or type_string.startswith("list") + ): if json_payload is not None: return json_payload inner_type = _extract_type_of(type_string)