Make engine event payloads JSON safe

This commit is contained in:
Thigazhezhilan J 2026-04-15 09:54:35 +05:30
parent a90603f4f6
commit 247a1c5107
2 changed files with 65 additions and 11 deletions

View File

@ -112,3 +112,39 @@ def test_bootstrap_schema_contains_migrated_core_columns_and_tables():
for snippet in required_snippets:
assert snippet in schema_sql
def test_insert_engine_event_serializes_nested_datetimes():
from indian_paper_trading_strategy.engine.db import insert_engine_event
class FakeCursor:
def __init__(self):
self.params = None
def execute(self, _sql, params):
self.params = params
cursor = FakeCursor()
event_ts = datetime(2026, 4, 15, 9, 0, tzinfo=timezone.utc)
payload_ts = datetime(2026, 4, 15, 9, 1, tzinfo=timezone.utc)
insert_engine_event(
cursor,
"ORDER_PLACED",
data={
"order_id": "order-1",
"timestamp": payload_ts,
"history": [payload_ts],
},
meta={"checked_at": payload_ts},
ts=event_ts,
user_id="user-1",
run_id="run-1",
)
data_json = cursor.params[4]
meta_json = cursor.params[6]
assert data_json.adapted["timestamp"] == payload_ts.isoformat()
assert data_json.adapted["history"] == [payload_ts.isoformat()]
assert meta_json.adapted["checked_at"] == payload_ts.isoformat()

View File

@ -2,8 +2,10 @@ import os
import threading
import time
from contextlib import contextmanager
from datetime import datetime, timedelta, timezone
from datetime import date, datetime, timedelta, timezone
from contextvars import ContextVar
from decimal import Decimal
from uuid import UUID
import psycopg2
from psycopg2 import pool
@ -146,6 +148,22 @@ def _utc_now():
return datetime.utcnow().replace(tzinfo=timezone.utc)
def _json_safe(value):
if isinstance(value, datetime):
return value.isoformat()
if isinstance(value, date):
return value.isoformat()
if isinstance(value, Decimal):
return float(value)
if isinstance(value, UUID):
return str(value)
if isinstance(value, dict):
return {str(key): _json_safe(item) for key, item in value.items()}
if isinstance(value, (list, tuple, set)):
return [_json_safe(item) for item in value]
return value
def set_context(user_id: str | None, run_id: str | None):
token_user = _USER_ID.set(user_id)
token_run = _RUN_ID.set(run_id)
@ -328,9 +346,9 @@ def insert_engine_event(
scope_run,
when,
event,
Json(data) if data is not None else None,
Json(_json_safe(data)) if data is not None else None,
message,
Json(meta) if meta is not None else None,
Json(_json_safe(meta)) if meta is not None else None,
),
)