diff --git a/GEMINI.md b/GEMINI.md index 2c2cab7..276fb7c 100644 --- a/GEMINI.md +++ b/GEMINI.md @@ -46,10 +46,17 @@ I am an interactive CLI agent assisting with software engineering tasks for One ## Session Learnings & Progress (Jan 2-3, 2026) -- **Logging Robustness:** All core modules and routers now use module-level loggers (`logging.getLogger(__name__)`). `app/log.py` includes robust `dictConfig` initialization with error handling. -- **Backward Compatibility:** Hybrid object definitions ensure that `/v2/crud` continues to work by including both modern (`tbl`, `mdl`) and legacy (`table_name`, `base_name`) keys. -- **FastAPI Best Practices:** Standardized `Response` injection via `response: Response` type hints instead of `Depends(Response)`. -- **Documentation:** Created `V3_FRONTEND_API_GUIDE.md` for Svelte/TypeScript integration and `V3_CRUD_ARCHITECTURE_AND_LEARNINGS.md` for backend maintenance. +### V3 CRUD Infrastructure & Security +- **Modular Object Definitions**: Successfully refactored the monolithic `ae_obj_types_def.py` into a domain-driven structure under `app/object_definitions/`. This improved maintainability while keeping legacy V2 keys for backward compatibility. +- **Advanced Search (POST)**: Implemented a robust `/search` endpoint supporting recursive AND/OR logic and standardized full-text search via the `q` property. +- **Security Hardening**: Enforced a 5-level recursion depth limit and a field allowlist (`searchable_fields`) per object to prevent unauthorized data leaks. +- **JWT Authentication**: Implemented modern JWT validation for V3, supporting both the `Authorization` header and a `jwt` query parameter (enabling secure, header-free file downloads). +- **Frontend Integration**: Created a dedicated `V3_FRONTEND_API_GUIDE.md` to help the Svelte Gemini agent and developers migrate to the new endpoints. + +### Technical & Environment Stability +- **Robust Logging**: Standardized on module-level loggers and wrapped logging configuration in `try/except` to prevent Docker startup crashes. +- **Circular Dependency Resolution**: Identified and resolved a major circular dependency loop between `lib_general_v3`, `response_models`, and `db_sql`. +- **FastAPI Standards**: Fixed `Response` injection and parameter ordering issues that were causing "Worker failed to boot" errors. ## Current To-Do List diff --git a/app/db_sql.py b/app/db_sql.py index bdb89d7..e11a990 100644 --- a/app/db_sql.py +++ b/app/db_sql.py @@ -2140,7 +2140,13 @@ def sql_search_qry_part( "like": "LIKE", "in": "IN", "is_null": "IS NULL", - "is_not_null": "IS NOT NULL" + "is_not_null": "IS NOT NULL", + "contains": "LIKE", + "icontains": "LIKE", + "startswith": "LIKE", + "istartswith": "LIKE", + "endswith": "LIKE", + "iendswith": "LIKE" } def process_node(query_node, current_depth: int) -> str: @@ -2188,17 +2194,30 @@ def sql_search_qry_part( if searchable_fields is not None and f.field not in searchable_fields: raise HTTPException(status_code=400, detail=f"Searching on field '{f.field}' is not permitted.") - sql_op = operator_map.get(f.op.lower()) + op_lower = f.op.lower() + sql_op = operator_map.get(op_lower) if not sql_op: - raise ValueError(f"Unsupported search operator: {f.op}") + raise HTTPException(status_code=400, detail=f"Unsupported search operator: {f.op}") filter_data = {} - if f.op.lower() in ['is_null', 'is_not_null']: + if op_lower in ['is_null', 'is_not_null']: clause = f"`{f.field}` {sql_op}" - elif f.op.lower() == 'in': + elif op_lower == 'in': p_name = get_param_name() clause = f"`{f.field}` IN (:{p_name})" filter_data[p_name] = f.value + elif op_lower in ['contains', 'icontains']: + p_name = get_param_name() + clause = f"`{f.field}` LIKE :{p_name}" + filter_data[p_name] = f"%{f.value}%" + elif op_lower in ['startswith', 'istartswith']: + p_name = get_param_name() + clause = f"`{f.field}` LIKE :{p_name}" + filter_data[p_name] = f"{f.value}%" + elif op_lower in ['endswith', 'iendswith']: + p_name = get_param_name() + clause = f"`{f.field}` LIKE :{p_name}" + filter_data[p_name] = f"%{f.value}" else: p_name = get_param_name() clause = f"`{f.field}` {sql_op} :{p_name}" diff --git a/app/models/api_crud_models.py b/app/models/api_crud_models.py index 9dde73d..755320a 100644 --- a/app/models/api_crud_models.py +++ b/app/models/api_crud_models.py @@ -16,7 +16,7 @@ class SearchFilter(BaseModel): Example: {"field": "price", "op": "gt", "value": 100} """ field: str - op: str # eq, ne, gt, gte, lt, lte, like, in, is_null, is_not_null + op: str # eq, ne, gt, gte, lt, lte, like, in, is_null, is_not_null, contains, startswith, endswith value: Optional[Any] = None class SearchQuery(BaseModel): diff --git a/documentation/V3_CRUD_ARCHITECTURE_AND_LEARNINGS.md b/documentation/V3_CRUD_ARCHITECTURE_AND_LEARNINGS.md index 0bedc7f..4d02fe1 100644 --- a/documentation/V3_CRUD_ARCHITECTURE_AND_LEARNINGS.md +++ b/documentation/V3_CRUD_ARCHITECTURE_AND_LEARNINGS.md @@ -18,7 +18,7 @@ The V3 CRUD API (`/v3/crud/`) is designed to run in parallel with legacy V1 and - **Data-Driven Configuration**: Uses the modern format in `app/ae_obj_types_def.py` to map objects to tables and models. - **Advanced Search (POST)**: Supports complex, nested filtering via `POST /v3/crud/{obj_type}/search`. - Recursive AND/OR logic. - - Full operator support: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `like`, `in`, `is_null`, `is_not_null`. + - Full operator support: `eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `is_null`, `is_not_null`, `like`, `contains`, `startswith`, `endswith`. - Safe parameterization using unique generated names (e.g., `:sp_1`) to prevent collisions. ## 2. Backward Compatibility Strategy diff --git a/documentation/V3_FRONTEND_API_GUIDE.md b/documentation/V3_FRONTEND_API_GUIDE.md index b98ee14..c5719e4 100644 --- a/documentation/V3_FRONTEND_API_GUIDE.md +++ b/documentation/V3_FRONTEND_API_GUIDE.md @@ -73,6 +73,25 @@ Use the `q` property in your search body for a general keyword search across ind } ``` +### D. Supported Search Operators +The `op` property in a `SearchFilter` supports the following values: + +| Operator | SQL equivalent | Description | +| --- | --- | --- | +| `eq` | `=` | Equal to | +| `ne` | `!=` | Not equal to | +| `gt` | `>` | Greater than | +| `gte` | `>=` | Greater than or equal to | +| `lt` | `<` | Less than | +| `lte` | `<=` | Less than or equal to | +| `in` | `IN (...)` | Matches any value in a provided list | +| `is_null` | `IS NULL` | Field is null (ignores `value`) | +| `is_not_null` | `IS NOT NULL` | Field is not null (ignores `value`) | +| `like` | `LIKE` | Standard SQL LIKE (requires manual `%` in `value`) | +| `contains` | `LIKE %val%` | Wraps value in `%` automatically | +| `startswith` | `LIKE val%` | Appends `%` to value automatically | +| `endswith` | `LIKE %val` | Prepends `%` to value automatically | + --- ## 4. Authentication in V3