New CRUD PATCH endpoint

This commit is contained in:
Scott Idem
2022-11-28 19:40:57 -05:00
parent 856e2a2891
commit 188947ecad
3 changed files with 184 additions and 83 deletions

View File

@@ -351,15 +351,22 @@ def sql_update(
else: else:
log.debug(result_update) log.debug(result_update)
log.debug(f'rowcount = {result_update.rowcount}; lastrowid = {result_update.lastrowid}') log.debug(f'rowcount = {result_update.rowcount}; lastrowid = {result_update.lastrowid}')
if result_update.rowcount >= 1 and result_update.lastrowid == 0: # update with no change if result_update.rowcount >= 1 and result_update.lastrowid == 0: # one record updated
log.info(f'Updated {result_update.rowcount} records (with no changes?)') # With SQL UPDATE this record may have actually changed log.info(f'One record was found and updated (changes unknown). Returning True') # With SQL UPDATE this record may have actually changed
return True return True
elif result_update.rowcount > 1 and result_update.lastrowid == 0: # multiple records updated
log.info(f'Multiple records ({result_update.rowcount}) were found and updated. Returning True')
return True
elif result_update.rowcount == 0 and result_update.lastrowid == 0: # no records found to update (ID probably not found)
log.info('No record(s) found to update. The ID was probably not found. Returning None')
return None
elif result_update.rowcount == 2 and result_update.lastrowid > 0: # update with change elif result_update.rowcount == 2 and result_update.lastrowid > 0: # update with change
log.warning('Should we be here???') log.warning('Should we be here???')
log.info('Update record with changes') log.info('Update record with changes')
record_id = result_update.lastrowid record_id = result_update.lastrowid
return record_id return record_id
else: else:
log.info('Unknown or unexpected SQL UPDATE response? Returning None')
log.debug(result_update) log.debug(result_update)
log.debug(vars(result_update)) log.debug(vars(result_update))
log.debug(dir(result_update)) log.debug(dir(result_update))

View File

@@ -0,0 +1,68 @@
import datetime, pytz
from typing import Dict, List, Optional, Set, Union
from pydantic import BaseModel, EmailStr, Field, Json, PrivateAttr, ValidationError, validator
from app.db_sql import redis_lookup_id_random
from app.lib_general import log, logging
from app.models.common_field_schema import base_fields, default_num_bytes
# ### BEGIN ### API CRUD Models ### Fundraising_Cfg_Base() ###
class Api_Crud_Base(BaseModel):
log.setLevel(logging.INFO) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
log.debug(locals())
super_key: Optional[str] = None # Query(None, min_length=8, max_length=50),
create_key: Optional[str] = None # Query(None, min_length=6, max_length=50),
read_key: Optional[str] = None # Query(None, min_length=5, max_length=50),
update_key: Optional[str] = None # Query(None, min_length=6, max_length=50),
delete_key: Optional[str] = None # Query(None, min_length=7, max_length=50),
# id_random: Optional[str] = Field(
# alias = 'obj_id_random',
# )
# id: Optional[int] = Field(
# alias = 'obj_id',
# )
obj_type: Optional[str]
# account_id_random: Optional[str]
# account_id: Optional[int]
data_list: Optional[dict]
# enable: Optional[bool]
# hide: Optional[bool]
# priority: Optional[bool]
# sort: Optional[int]
# group: Optional[str] # Same or similar as file_purpose?
# created_on: Optional[datetime.datetime] = None
# updated_on: Optional[datetime.datetime] = None
_processed_at: datetime.datetime = PrivateAttr(default_factory=datetime.datetime.now)
# @validator('id', always=True)
# def fundraising_cfg_id_lookup(cls, v, values, **kwargs):
# if isinstance(v, int) and v > 0: return v
# elif id_random := values.get('id_random'):
# return redis_lookup_id_random(record_id_random=id_random, table_name='v_fundraising_cfg') # There is only a view 2022-11-18
# return None
# @validator('account_id', always=True)
# def account_id_lookup(cls, v, values, **kwargs):
# if isinstance(v, int) and v > 0: return v
# elif id_random := values.get('account_id_random'):
# return redis_lookup_id_random(record_id_random=id_random, table_name='account')
# return None
class Config:
underscore_attrs_are_private = True
allow_population_by_field_name = True
fields = base_fields
# ### END ### API CRUD Models ### Api_Crud_Base() ###

View File

@@ -1,4 +1,4 @@
import datetime import datetime, time
#from datetime import datetime, time, timedelta #from datetime import datetime, time, timedelta
from fastapi import APIRouter, Body, Depends, Header, HTTPException, Query, Response, status from fastapi import APIRouter, Body, Depends, Header, HTTPException, Query, Response, status
from pydantic import BaseModel, EmailStr, Field from pydantic import BaseModel, EmailStr, Field
@@ -10,6 +10,8 @@ from app.db_sql import sql_insert, sql_update, sql_insert_or_update, sql_select,
from app.models.response_models import * from app.models.response_models import *
from app.models.api_crud_models import *
from app.models.account_models import * from app.models.account_models import *
from app.models.account_cfg_models import * from app.models.account_cfg_models import *
from app.models.activity_log_models import * from app.models.activity_log_models import *
@@ -325,96 +327,120 @@ async def get_obj(
return mk_resp(data=False, status_code=404, response=response) return mk_resp(data=False, status_code=404, response=response)
# @router.patch('/{obj_type_l1}/{obj_id}') @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_id}')
# @router.patch('/{obj_type_l1}/{obj_type_l2}/{obj_type_l3}/{obj_id}') @router.patch('/{obj_type_l1}/{obj_type_l2}/{obj_type_l3}/{obj_id}')
# async def patch_obj( async def patch_obj(
# obj_type_l1: Optional[str] = Query(..., max_length=50), crud: Api_Crud_Base,
# obj_type_l2: str=None, obj_type_l1: Optional[str] = Query(..., max_length=50),
# obj_type_l3: str=None, obj_type_l2: str = None,
# obj_id: str = Query(..., min_length=11, max_length=22), obj_type_l3: str = None,
obj_id: str = Query(..., min_length=11, max_length=22),
# for_obj_type: Optional[str] = Query(None, max_length=50),
# for_obj_id: Optional[str] = Query(None, max_length=22),
# x_account_id: str = Header(...), # for_obj_type: Optional[str] = Query(None, max_length=50),
# qry_str: Optional[str] = Query(None, max_length=50), # for_obj_id: Optional[str] = Query(None, max_length=22),
# qry_int: Optional[int] = None,
# by_alias: Optional[bool] = True,
# include: Optional[list] = [],
# exclude: Optional[list] = [],
# exclude_unset: Optional[bool] = True,
# exclude_none: Optional[bool] = True,
# response: Response = Response,
# commons: Common_Route_Params = Depends(common_route_params), commons: Common_Route_Params = Depends(common_route_params),
# ): ):
# """ """
# Simple select object type with an ID: Simple patch object type with an ID:
# - **obj_type_l1, obj_type_l2, obj_type_l3**: - **obj_type_l1, obj_type_l2, obj_type_l3**:
# - Examples: - Examples:
# - /account = account - /account = account
# - /user = user - /user = user
# - /user/role = user_role - /user/role = user_role
# - /event = event - /event = event
# - /event/exhibit = event_exhibit - /event/exhibit = event_exhibit
# - /order = order - /order = order
# - /order/cart = order_cart - /order/cart = order_cart
# - /order/cart/line = order_cart_line - /order/cart/line = order_cart_line
# - /lu/some_lookup = lu_some_lookup - /lu/some_lookup = lu_some_lookup
# """ """
# log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL
# log.debug(locals()) log.debug(locals())
# debug_data = {} if crud.super_key == '789123': pass
# debug_data['obj_type_l1'] = obj_type_l1 else:
# debug_data['obj_type_l2'] = obj_type_l2 log.warning('Access key is missing or incorrect')
# debug_data['obj_type_l3'] = obj_type_l3 return mk_resp(data=False, status_code=400, response=commons.response)
# debug_data['obj_id'] = obj_id
# debug_data['for_obj_type'] = for_obj_type
# debug_data['for_obj_id'] = for_obj_id
# log.debug(debug_data) # NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING
# time.sleep(1.5) # NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING
# NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING NOTE: WARNING
# if obj_type_l1 and obj_type_l2 and obj_type_l3: debug_data = {}
# obj_name = f'{obj_type_l1}_{obj_type_l2}_{obj_type_l3}' debug_data['crud'] = crud
# if obj_name in obj_type_li:
# #table_name = obj_type_li[obj_name]
# #table_name = obj_type_li[obj_name]['table_name']
# pass
# else:
# return mk_resp(data=False, status_code=400, response=commons.response)
# elif obj_type_l1 and obj_type_l2:
# obj_name = f'{obj_type_l1}_{obj_type_l2}'
# if obj_name in obj_type_li:
# #table_name = obj_type_li[obj_name]['table_name']
# pass
# else:
# return mk_resp(data=False, status_code=400, response=commons.response)
# elif obj_type_l1:
# obj_name = f'{obj_type_l1}'
# if obj_name in obj_type_li:
# #table_name = obj_type_li[obj_name]['table_name']
# pass
# else:
# return mk_resp(data=False, status_code=400, response=commons.response)
# else:
# 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]['table_name'] debug_data['create_key'] = crud.create_key
debug_data['read_key'] = crud.read_key
debug_data['update_key'] = crud.update_key
debug_data['delete_key'] = crud.delete_key
# # NOTE: Add a check for the object ID... assuming it is a random ID string for now. debug_data['data_list'] = crud.data_list
# if sql_result := sql_select(table_name=table_name, record_id_random=obj_id):
# log.debug(sql_result)
# base_name = obj_type_li[obj_name]['base_name'] debug_data['obj_type_l1'] = obj_type_l1
# resp_data = base_name(**sql_result).dict(by_alias=commons.by_alias, exclude_unset=commons.exclude_unset) debug_data['obj_type_l2'] = obj_type_l2
debug_data['obj_type_l3'] = obj_type_l3
debug_data['obj_id'] = obj_id
# return mk_resp(data=resp_data, response=response) #, details=debug_data) log.debug(debug_data)
# else:
# log.debug(sql_result) if obj_type_l1 and obj_type_l2 and obj_type_l3:
# return mk_resp(data=False, status_code=404, response=response) obj_name = f'{obj_type_l1}_{obj_type_l2}_{obj_type_l3}'
if obj_name in obj_type_li:
#table_name = obj_type_li[obj_name]
#table_name = obj_type_li[obj_name]['table_name']
pass
else:
return mk_resp(data=False, status_code=400, response=commons.response)
elif obj_type_l1 and obj_type_l2:
obj_name = f'{obj_type_l1}_{obj_type_l2}'
if obj_name in obj_type_li:
#table_name = obj_type_li[obj_name]['table_name']
pass
else:
return mk_resp(data=False, status_code=400, response=commons.response)
elif obj_type_l1:
obj_name = f'{obj_type_l1}'
if obj_name in obj_type_li:
#table_name = obj_type_li[obj_name]['table_name']
pass
else:
return mk_resp(data=False, status_code=400, response=commons.response)
else:
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]['table_name']
# ### SECTION ### Secondary data validation
obj_id_random = obj_id # This is 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.')
# obj_id = 999999
# NOTE: Add a check for the object ID... assuming it is a random ID string for now.
if sql_result := sql_update(data=crud.data_list, table_name=table_name, record_id=obj_id, log_lvl=logging.INFO):
log.info('The record was updated.')
log.debug(sql_result)
base_name = obj_type_li[obj_name]['base_name']
# resp_data = base_name(**sql_result).dict(by_alias=commons.by_alias, exclude_unset=commons.exclude_unset)
resp_data = { 'base_name': base_name, 'request_data': crud.data_list }
resp_data = 'Record updated!'
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)
return mk_resp(data=None, status_code=404, status_message='The record was probably not found to be updated.', response=commons.response)
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.')
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.', response=commons.response)