From 14c6cb8bc00d79699e35910909739936f993eeab Mon Sep 17 00:00:00 2001 From: Scott Idem Date: Fri, 8 Mar 2024 18:10:10 -0500 Subject: [PATCH] Work on CRUD POST and PATCH related. --- app/routers/api_crud.py | 97 ++++++++++++++++++++++++++++++++++------- 1 file changed, 82 insertions(+), 15 deletions(-) diff --git a/app/routers/api_crud.py b/app/routers/api_crud.py index 45bff9b..da34488 100644 --- a/app/routers/api_crud.py +++ b/app/routers/api_crud.py @@ -500,7 +500,7 @@ async def get_obj( # ### BEGIN ### API CRUD ### patch_obj() ### -# Updated 2022-11-29 +# Updated 2024-03-08 @router.patch('/{obj_type_l1}/{obj_id}') @router.patch('/{obj_type_l1}/{obj_type_l2}/{obj_id}') @router.patch('/{obj_type_l1}/{obj_type_l2}/{obj_type_l3}/{obj_id}') @@ -516,7 +516,9 @@ async def patch_obj( # for_obj_type: Optional[str] = Query(None, max_length=50), # for_obj_id: Optional[str] = Query(None, max_length=22), - # return_obj: Optional[bool] = True, # I am not sure how to make this work yet. -2024-03-07 + # The view name will be prefixed with "v_" and must be a valid view name based on the object type name from the URL. obj_type_l1, obj_type_l2, obj_type_l3 combined below as obj_name + return_obj: Optional[bool] = True, # I am not sure how to make this work yet. -2024-03-08 + obj_v_name: Optional[str] = None, # Use view name to help return the object type. -2024-03-08 commons: Common_Route_Params = Depends(common_route_params), ): @@ -597,7 +599,7 @@ async def patch_obj( exclude = obj_type_li[obj_name].get('exclude_for_db') # ### SECTION ### Secondary data validation - # obj_id_random = obj_id # This might need to be used later for the response data + obj_id_random = obj_id # This might need to be used later for the response data if obj_id := redis_lookup_id_random(record_id_random=obj_id, table_name=table_name): pass else: return mk_resp(data=None, status_code=404, response=commons.response, status_message='The object ID was invalid or not found.') @@ -636,8 +638,11 @@ async def patch_obj( resp_data = {} resp_data['table_name'] = table_name resp_data['request_data'] = obj_dict + resp_data['obj_id'] = obj_id + resp_data['obj_id_random'] = obj_id_random - return mk_resp(data=resp_data, response=commons.response) #, details=debug_data) + # Comment out the following line if you do not want to allo for returning the updated data. + # return mk_resp(data=resp_data, response=commons.response) #, details=debug_data) elif sql_result == None: log.info('The record was probably not found to be updated.') log.debug(sql_result) @@ -649,18 +654,45 @@ async def patch_obj( # ### SECTION ### Return successful results - # if return_obj: - # data_store_obj = load_data_store_obj( - # data_store_id = data_store_id, - # ) - # resp_data = data_store_obj - # return mk_resp(data=resp_data, response=commons.response) #, details=debug_data) + # NOTE: obj_id was found and verified above + # New: 2024-03-08 + if return_obj: + if obj_v_name: + # We should add a sanity check here to make sure the view name is valid first. + table_name = f'v_{obj_v_name}' + # Remove an extra "v_" if it is there. + if table_name.startswith('v_v_'): + table_name = table_name[2:] + # We can do more later... like check against an allowed list of view names per Aether object type + else: + # Use the table_name as a backup option? This should be safe since it is also passed through the model. + table_name = obj_type_li[obj_name]['table_name'] + + base_name = obj_type_li[obj_name]['base_name'] + + if sql_select_result := sql_select(table_name=table_name, record_id=obj_id): + log.debug(sql_select_result) + + log.debug(base_name) + + resp_data = base_name(**sql_select_result).dict(by_alias=True, exclude_unset=commons.exclude_unset) + + log.debug(resp_data) + + log.info(f'Returning object model {base_name} from table_name {table_name}; obj_id; obj_id_random={obj_id_random}) using PATCH data') + return mk_resp(data=resp_data, response=commons.response) + else: + log.debug(sql_select_result) + status_message = f'The record was not found in table_name={table_name} used for the SQL SELECT query. This may be a SQL VIEW and may be because the SQL VIEW needs to be created or updated.' + return mk_resp(data=False, status_code=404, response=commons.response, status_message=status_message) + log.info(f'Returning IDs (obj_id, obj_id_random={obj_id_random}) using PATCH data') + return mk_resp(data=resp_data, response=commons.response) #, details=debug_data) # ### END ### API CRUD ### patch_obj() ### # ### BEGIN ### API CRUD ### post_obj() ### -# Updated 2022-11-29 +# Updated 2024-03-08 @router.post('/{obj_type_l1}') @router.post('/{obj_type_l1}/{obj_type_l2}') @router.post('/{obj_type_l1}/{obj_type_l2}/{obj_type_l3}') @@ -673,10 +705,12 @@ async def post_obj( run_safety_check: bool = True, + # The view name will be prefixed with "v_" and must be a valid view name based on the object type name from the URL. obj_type_l1, obj_type_l2, obj_type_l3 combined below as obj_name # for_obj_type: Optional[str] = Query(None, max_length=50), # for_obj_id: Optional[str] = Query(None, max_length=22), - return_obj: bool = True, + return_obj: Optional[bool] = True, + obj_v_name: Optional[str] = None, # Use view name to help return the object type. -2024-03-08 commons: Common_Route_Params = Depends(common_route_params), ): @@ -753,8 +787,37 @@ async def post_obj( log.warning('We should not be here') return mk_resp(data=False, status_code=400, response=commons.response) - table_name = obj_type_li[obj_name].get('tbl_name_update') - table_name_select = obj_type_li[obj_name].get('table_name') + + # ### SECTION ### Figure out the table_name to use + # Updated: 2024-03-08 + view_name = None # This is the view name to use for the SQL SELECT query + if obj_v_name: + # We should add a sanity check here to make sure the view name is valid first. + view_name = f'v_{obj_v_name}' + # Remove an extra "v_" if it is there. + if view_name.startswith('v_v_'): + view_name = view_name[2:] + # We can do more later... like check against an allowed list of view names per Aether object type + elif obj_type_li[obj_name].get('table_name'): + # Use the table_name as a backup option? This should be safe. + view_name = obj_type_li[obj_name].get('table_name') + elif obj_type_li[obj_name].get('table_name_alt'): + view_name = obj_type_li[obj_name].get('table_name_alt') + elif obj_type_li[obj_name].get('tbl_name_update'): + view_name = obj_type_li[obj_name].get('tbl_name_update') + else: + log.error('The table_name was not found. This is a critical error. Returning False.') + return mk_resp(data=False, status_code=400, response=commons.response, status_message='The table_name was not found. This is a critical error.') + + table_name = None # This is the table name to use for the SQL INSERT or UPDATE + if obj_type_li[obj_name].get('tbl_name_update'): + table_name = obj_type_li[obj_name].get('tbl_name_update') + elif obj_type_li[obj_name].get('table_name'): + table_name = obj_type_li[obj_name].get('table_name') + + # This should be a view in most cases. + table_name_select = view_name + exclude = obj_type_li[obj_name].get('exclude_for_db') @@ -803,8 +866,11 @@ async def post_obj( obj_id_random = get_id_random(record_id=obj_id, table_name=table_name) resp_data['obj_id_random'] = obj_id_random + # NOTE: obj_id was found and verified above + # Updated: 2024-03-08 if return_obj: log.info('Returning object created from POST data') + # This is the more or less the same as what is done in the patch_obj() endpoint. if sql_select_result := sql_select(table_name=table_name_select, record_id=obj_id): log.debug(sql_select_result) @@ -814,6 +880,7 @@ async def post_obj( log.debug(resp_data) + log.info(f'Returning object model ({base_name}; obj_id; obj_id_random={obj_id_random}) from POST data') return mk_resp(data=resp_data, response=commons.response) else: log.debug(sql_select_result) @@ -828,7 +895,7 @@ async def post_obj( else: log.info('Something unexpected happened while trying to runt he SQL UPDATE. The fields or field values passed may not be valid for the table and or model.') log.debug(sql_result) - return mk_resp(data=False, status_code=400, status_message='Something unexpected happened while trying to runt he SQL UPDATE. The fields or field values passed may not be valid for the table and or model.', response=commons.response) + return mk_resp(data=False, status_code=400, status_message='Something unexpected happened while trying to run the SQL UPDATE. The fields or field values passed may not be valid for the table and or model.', response=commons.response) # ### END ### API CRUD ### post_obj() ###