From 98b980cf2ba8a22130e00acbc60215c23762976a Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Wed, 3 Dec 2025 18:44:14 -0500 Subject: [PATCH] The basics are now working for v3. --- app/routers/api_crud_v3.py | 128 +++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) diff --git a/app/routers/api_crud_v3.py b/app/routers/api_crud_v3.py index 18f4a85..bc8ebce 100644 --- a/app/routers/api_crud_v3.py +++ b/app/routers/api_crud_v3.py @@ -539,6 +539,134 @@ async def get_child_obj( return mk_resp(data=False, status_code=404, response=commons.response, status_message=f"Child object with ID '{child_obj_id}' not found in database.") +@router.patch('/{parent_obj_type}/{parent_obj_id}/{child_obj_type}/{child_obj_id}', response_model=Resp_Body_Base) +async def patch_child_obj( + request: Request, + parent_obj_type: str = Path(min_length=2, max_length=50), + parent_obj_id: str = Path(min_length=11, max_length=22), + child_obj_type: str = Path(min_length=2, max_length=50), + child_obj_id: str = Path(min_length=11, max_length=22), + return_obj: Optional[bool] = True, + commons: Common_Route_Params = Depends(common_route_params), + ): + """ + Update a child object by its ID, ensuring it belongs to the correct parent. + Examples: + - PATCH /v3/crud/journal/{journal_id}/journal_entry/{entry_id} + """ + log.setLevel(logging.WARNING) + log.debug(locals()) + + obj_data = await request.json() + + parent_obj_name = parent_obj_type + child_obj_name = child_obj_type + + if parent_obj_name not in obj_type_kv_li: + return mk_resp(data=False, status_code=400, response=commons.response, status_message=f"Parent object type '{parent_obj_name}' not found.") + if child_obj_name not in obj_type_kv_li: + return mk_resp(data=False, status_code=400, response=commons.response, status_message=f"Child object type '{child_obj_name}' not found.") + + # Resolve IDs + resolved_parent_id = redis_lookup_id_random(record_id_random=parent_obj_id, table_name=parent_obj_name) + if not resolved_parent_id: + return mk_resp(data=False, status_code=404, response=commons.response, status_message=f"Parent object '{parent_obj_name}' with ID '{parent_obj_id}' not found.") + + resolved_child_id = redis_lookup_id_random(record_id_random=child_obj_id, table_name=child_obj_name) + if not resolved_child_id: + return mk_resp(data=False, status_code=404, response=commons.response, status_message=f"Child object '{child_obj_name}' with ID '{child_obj_id}' not found.") + + # Get config for child object + obj_cfg = obj_type_kv_li[child_obj_name] + table_name_update = obj_cfg.get('tbl_update', obj_cfg.get('tbl')) + table_name_select = obj_cfg.get('tbl_default', obj_cfg.get('tbl')) + output_model = obj_cfg.get('mdl_out', obj_cfg.get('mdl_default', obj_cfg.get('mdl'))) + + if not table_name_update or not table_name_select or not output_model: + return mk_resp(data=False, status_code=500, response=commons.response, status_message=f"Configuration for child object type '{child_obj_name}' is incomplete.") + + # Verify parentage before updating + if existing_child := sql_select(table_name=table_name_select, record_id=resolved_child_id): + parent_fk_field_name = f'{parent_obj_name}_id' + if existing_child.get(parent_fk_field_name) != resolved_parent_id: + return mk_resp(data=False, status_code=404, response=commons.response, status_message=f"Child object '{child_obj_id}' not found under parent '{parent_obj_id}'.") + else: + return mk_resp(data=False, status_code=404, response=commons.response, status_message=f"Child object '{child_obj_id}' not found.") + + # The sql_update function will only update the fields provided in the dict. + data_to_update = obj_data + + if sql_update(data=data_to_update, table_name=table_name_update, record_id=resolved_child_id): + if return_obj: + if updated_child := sql_select(table_name=table_name_select, record_id=resolved_child_id): + resp_data = output_model(**updated_child).dict(by_alias=commons.by_alias, exclude_unset=commons.exclude_unset) + return mk_resp(data=resp_data, response=commons.response) + else: + return mk_resp(data=True, status_code=404, response=commons.response, status_message="Object updated but could not be retrieved post-update.") + else: + return mk_resp(data=True, response=commons.response, status_message="Object updated successfully.") + else: + return mk_resp(data=False, status_code=400, response=commons.response, status_message="Failed to update object in database.") + + +@router.delete('/{parent_obj_type}/{parent_obj_id}/{child_obj_type}/{child_obj_id}', response_model=Resp_Body_Base) +async def delete_child_obj( + parent_obj_type: str = Path(min_length=2, max_length=50), + parent_obj_id: str = Path(min_length=11, max_length=22), + child_obj_type: str = Path(min_length=2, max_length=50), + child_obj_id: str = Path(min_length=11, max_length=22), + commons: Common_Route_Params = Depends(common_route_params), + ): + """ + Delete a child object by its ID, ensuring it belongs to the correct parent. + Examples: + - DELETE /v3/crud/journal/{journal_id}/journal_entry/{entry_id} + """ + log.setLevel(logging.WARNING) + log.debug(locals()) + + parent_obj_name = parent_obj_type + child_obj_name = child_obj_type + + if parent_obj_name not in obj_type_kv_li: + return mk_resp(data=False, status_code=400, response=commons.response, status_message=f"Parent object type '{parent_obj_name}' not found.") + if child_obj_name not in obj_type_kv_li: + return mk_resp(data=False, status_code=400, response=commons.response, status_message=f"Child object type '{child_obj_name}' not found.") + + # Resolve IDs + resolved_parent_id = redis_lookup_id_random(record_id_random=parent_obj_id, table_name=parent_obj_name) + if not resolved_parent_id: + return mk_resp(data=False, status_code=404, response=commons.response, status_message=f"Parent object '{parent_obj_name}' with ID '{parent_obj_id}' not found.") + + resolved_child_id = redis_lookup_id_random(record_id_random=child_obj_id, table_name=child_obj_name) + if not resolved_child_id: + return mk_resp(data=False, status_code=404, response=commons.response, status_message=f"Child object '{child_obj_name}' with ID '{child_obj_id}' not found.") + + # Get config for child object + obj_cfg = obj_type_kv_li[child_obj_name] + table_name_delete = obj_cfg.get('tbl_update', obj_cfg.get('tbl')) + table_name_select = obj_cfg.get('tbl_default', obj_cfg.get('tbl')) # For verification + + if not table_name_delete or not table_name_select: + return mk_resp(data=False, status_code=500, response=commons.response, status_message=f"Configuration for child object type '{child_obj_name}' is incomplete.") + + # Verify parentage before deleting + if existing_child := sql_select(table_name=table_name_select, record_id=resolved_child_id): + parent_fk_field_name = f'{parent_obj_name}_id' + if existing_child.get(parent_fk_field_name) != resolved_parent_id: + return mk_resp(data=False, status_code=404, response=commons.response, status_message=f"Child object '{child_obj_id}' not found under parent '{parent_obj_id}'.") + else: + return mk_resp(data=False, status_code=404, response=commons.response, status_message=f"Child object '{child_obj_id}' not found.") + + # If verification passes, delete the object + if sql_delete(table_name=table_name_delete, record_id=resolved_child_id): + return mk_resp(data=True, response=commons.response, status_message=f"Object with ID '{child_obj_id}' deleted successfully.") + else: + return mk_resp(data=False, status_code=400, response=commons.response, status_message="Failed to delete object in database.") + + + +