diff --git a/src/agent_toolkit/forestadmin/agent_toolkit/services/serializers/json_api_serializer.py b/src/agent_toolkit/forestadmin/agent_toolkit/services/serializers/json_api_serializer.py index 7d681bba..7dfb452d 100644 --- a/src/agent_toolkit/forestadmin/agent_toolkit/services/serializers/json_api_serializer.py +++ b/src/agent_toolkit/forestadmin/agent_toolkit/services/serializers/json_api_serializer.py @@ -1,5 +1,4 @@ from ast import literal_eval -from datetime import date, datetime, time from typing import Any, Callable, Dict, List, Optional, Tuple, Union, cast from urllib.parse import quote from uuid import uuid4 @@ -134,15 +133,18 @@ def number_dump(val): elif isinstance(val, str): return literal_eval(str(value)) + def date_types_dump(val): + return val if isinstance(val, str) else val.isoformat() + parser_map: Dict[PrimitiveType, Callable] = { PrimitiveType.STRING: str, PrimitiveType.ENUM: str, PrimitiveType.BOOLEAN: bool, PrimitiveType.NUMBER: number_dump, PrimitiveType.UUID: str, - PrimitiveType.DATE_ONLY: lambda v: v if isinstance(v, str) else date.isoformat(v), - PrimitiveType.TIME_ONLY: lambda v: v if isinstance(v, str) else time.isoformat(v), - PrimitiveType.DATE: lambda v: v if isinstance(v, str) else datetime.isoformat(v), + PrimitiveType.DATE_ONLY: date_types_dump, + PrimitiveType.TIME_ONLY: date_types_dump, + PrimitiveType.DATE: date_types_dump, PrimitiveType.POINT: lambda v: v, PrimitiveType.BINARY: lambda v: v, # should not be called, because of binary decorator this type # is transformed to string diff --git a/src/agent_toolkit/tests/services/serializers/test_jsonapi.py b/src/agent_toolkit/tests/services/serializers/test_jsonapi.py index 51a93545..f47713b4 100644 --- a/src/agent_toolkit/tests/services/serializers/test_jsonapi.py +++ b/src/agent_toolkit/tests/services/serializers/test_jsonapi.py @@ -1353,3 +1353,138 @@ def test_string_foreign_keys_should_be_url_encoded_so_foreign_pk(self): ], }, ) + + def test_datetime_should_also_serialize_dates_and_times(self): + serializer = JsonApiSerializer(self.datasource, Projection("all_types_pk", "datetime")) + record = { + "all_types_pk": "c578ccd6-3dd0-4315-87f3-e200d80dd6f9", + "datetime": date(2023, 10, 10), + } + # date + dumped = serializer.serialize(record, self.collection_all_types) + self.assertEqual( + dumped, + { + "data": { + "type": "AllTypes", + "id": "c578ccd6-3dd0-4315-87f3-e200d80dd6f9", + "attributes": { + "all_types_pk": "c578ccd6-3dd0-4315-87f3-e200d80dd6f9", + "datetime": "2023-10-10", + }, + "links": {"self": "/forest/AllTypes/c578ccd6-3dd0-4315-87f3-e200d80dd6f9"}, + }, + "links": {"self": "/forest/AllTypes/c578ccd6-3dd0-4315-87f3-e200d80dd6f9"}, + }, + ) + # time + record = { + "all_types_pk": "c578ccd6-3dd0-4315-87f3-e200d80dd6f9", + "datetime": time(12, 12, 12), + } + dumped = serializer.serialize(record, self.collection_all_types) + self.assertEqual( + dumped, + { + "data": { + "type": "AllTypes", + "id": "c578ccd6-3dd0-4315-87f3-e200d80dd6f9", + "attributes": { + "all_types_pk": "c578ccd6-3dd0-4315-87f3-e200d80dd6f9", + "datetime": "12:12:12", + }, + "links": {"self": "/forest/AllTypes/c578ccd6-3dd0-4315-87f3-e200d80dd6f9"}, + }, + "links": {"self": "/forest/AllTypes/c578ccd6-3dd0-4315-87f3-e200d80dd6f9"}, + }, + ) + + def test_date_should_also_serialize_datetimes_and_times(self): + serializer = JsonApiSerializer(self.datasource, Projection("all_types_pk", "dateonly")) + record = { + "all_types_pk": "c578ccd6-3dd0-4315-87f3-e200d80dd6f9", + "dateonly": datetime(2023, 10, 10, 12, 12, 12), + } + # datetime + dumped = serializer.serialize(record, self.collection_all_types) + self.assertEqual( + dumped, + { + "data": { + "type": "AllTypes", + "id": "c578ccd6-3dd0-4315-87f3-e200d80dd6f9", + "attributes": { + "all_types_pk": "c578ccd6-3dd0-4315-87f3-e200d80dd6f9", + "dateonly": "2023-10-10T12:12:12", + }, + "links": {"self": "/forest/AllTypes/c578ccd6-3dd0-4315-87f3-e200d80dd6f9"}, + }, + "links": {"self": "/forest/AllTypes/c578ccd6-3dd0-4315-87f3-e200d80dd6f9"}, + }, + ) + # time + record = { + "all_types_pk": "c578ccd6-3dd0-4315-87f3-e200d80dd6f9", + "dateonly": time(12, 12, 12), + } + dumped = serializer.serialize(record, self.collection_all_types) + self.assertEqual( + dumped, + { + "data": { + "type": "AllTypes", + "id": "c578ccd6-3dd0-4315-87f3-e200d80dd6f9", + "attributes": { + "all_types_pk": "c578ccd6-3dd0-4315-87f3-e200d80dd6f9", + "dateonly": "12:12:12", + }, + "links": {"self": "/forest/AllTypes/c578ccd6-3dd0-4315-87f3-e200d80dd6f9"}, + }, + "links": {"self": "/forest/AllTypes/c578ccd6-3dd0-4315-87f3-e200d80dd6f9"}, + }, + ) + + def test_time_only_should_also_serialize_dates_and_datetimes(self): + serializer = JsonApiSerializer(self.datasource, Projection("all_types_pk", "time_only")) + record = { + "all_types_pk": "c578ccd6-3dd0-4315-87f3-e200d80dd6f9", + "time_only": datetime(2023, 10, 10, 12, 12, 12), + } + # datetime + dumped = serializer.serialize(record, self.collection_all_types) + self.assertEqual( + dumped, + { + "data": { + "type": "AllTypes", + "id": "c578ccd6-3dd0-4315-87f3-e200d80dd6f9", + "attributes": { + "all_types_pk": "c578ccd6-3dd0-4315-87f3-e200d80dd6f9", + "time_only": "2023-10-10T12:12:12", + }, + "links": {"self": "/forest/AllTypes/c578ccd6-3dd0-4315-87f3-e200d80dd6f9"}, + }, + "links": {"self": "/forest/AllTypes/c578ccd6-3dd0-4315-87f3-e200d80dd6f9"}, + }, + ) + # date + record = { + "all_types_pk": "c578ccd6-3dd0-4315-87f3-e200d80dd6f9", + "time_only": date(2023, 10, 10), + } + dumped = serializer.serialize(record, self.collection_all_types) + self.assertEqual( + dumped, + { + "data": { + "type": "AllTypes", + "id": "c578ccd6-3dd0-4315-87f3-e200d80dd6f9", + "attributes": { + "all_types_pk": "c578ccd6-3dd0-4315-87f3-e200d80dd6f9", + "time_only": "2023-10-10", + }, + "links": {"self": "/forest/AllTypes/c578ccd6-3dd0-4315-87f3-e200d80dd6f9"}, + }, + "links": {"self": "/forest/AllTypes/c578ccd6-3dd0-4315-87f3-e200d80dd6f9"}, + }, + )