From 53d252b23d591bb2624802e8161fdb3a971dea1b Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Fri, 2 Jan 2026 20:24:51 -0500 Subject: [PATCH] Fix: Add robust JSON parsing for V3 query params and fix missing Any import causing startup failure. --- app/routers/api_crud_v3.py | 48 ++++++++++++++++++-------------------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/app/routers/api_crud_v3.py b/app/routers/api_crud_v3.py index 7c8bcb9..5c41b41 100644 --- a/app/routers/api_crud_v3.py +++ b/app/routers/api_crud_v3.py @@ -1,5 +1,5 @@ from fastapi import APIRouter, Depends, Header, HTTPException, Path, Query, Request, Response, status -from typing import Dict, List, Optional, Set, Union +from typing import Any, Dict, List, Optional, Set, Union import json import urllib.parse import time @@ -22,6 +22,19 @@ from app.db_sql import redis_lookup_id_random, sql_select, sql_insert, sql_updat router = APIRouter() +def safe_json_loads(json_str: Optional[str]) -> Any: + """ + Safely load a JSON string, handling 'undefined' or invalid formats + common in frontend URL parameters. + """ + if not json_str or json_str == 'undefined': + return None + try: + return json.loads(json_str) + except (json.JSONDecodeError, TypeError) as e: + log.warning(f"Failed to parse JSON string: {json_str}. Error: {e}") + return None + @router.get("/health", response_model=Resp_Body_Base) async def health_check( delay: DelayParams = Depends(get_delay_params), @@ -132,15 +145,9 @@ async def get_obj_li( and_like_dict_obj = None or_like_dict_obj = None and_in_dict_li_obj = None - jp_obj = None - - if jp: - try: - jp_obj = json.loads(urllib.parse.unquote(jp)) - except Exception as e: - log.warning(e) - return mk_resp(data=False, status_code=400, response=response, status_message='The JSON string was not formatted correctly.') - + + jp_obj = safe_json_loads(urllib.parse.unquote(jp)) if jp else None + if jp_obj: if jp_obj.get('qry'): qry_dict_li = jp_obj['qry'] if jp_obj.get('ft_qry'): @@ -154,8 +161,7 @@ async def get_obj_li( if jp_obj.get('and_in_li'): and_in_dict_li_obj = jp_obj['and_in_li'] - if order_by_li: - order_by_li = json.loads(order_by_li) + order_by_li = safe_json_loads(order_by_li) obj_name = obj_type_l1 if obj_name not in obj_type_kv_li: @@ -253,8 +259,7 @@ async def search_obj_li( log.setLevel(logging.WARNING) log.debug(locals()) - if order_by_li: - order_by_li = json.loads(order_by_li) + order_by_li = safe_json_loads(order_by_li) obj_name = obj_type_l1 if obj_name not in obj_type_kv_li: @@ -521,15 +526,9 @@ async def get_child_obj_li( and_like_dict_obj = None or_like_dict_obj = None and_in_dict_li_obj = None - jp_obj = None - - if jp: - try: - jp_obj = json.loads(urllib.parse.unquote(jp)) - except Exception as e: - log.warning(e) - return mk_resp(data=False, status_code=400, response=response, status_message='The JSON string was not formatted correctly.') - + + jp_obj = safe_json_loads(urllib.parse.unquote(jp)) if jp else None + if jp_obj: if jp_obj.get('qry'): qry_dict_li = jp_obj['qry'] if jp_obj.get('ft_qry'): @@ -543,8 +542,7 @@ async def get_child_obj_li( if jp_obj.get('and_in_li'): and_in_dict_li_obj = jp_obj['and_in_li'] - if order_by_li: - order_by_li = json.loads(order_by_li) + order_by_li = safe_json_loads(order_by_li) obj_name = child_obj_type if obj_name not in obj_type_kv_li: