diff --git a/app/db_sql.py b/app/db_sql.py index c6234b0..35914cd 100644 --- a/app/db_sql.py +++ b/app/db_sql.py @@ -25,7 +25,7 @@ db = engine.connect() # ### BEGIN ### Core Help CRUD ### sql_insert() ### def sql_insert(sql:str=None, data:dict=None, table_name:str=None, id_random_length:int=8): - log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) if sql: @@ -81,7 +81,7 @@ def sql_insert(sql:str=None, data:dict=None, table_name:str=None, id_random_leng return False else: log.debug(result_insert) - log.debug(result_insert.rowcount) + log.debug(f'rowcount = {result_insert.rowcount}; lastrowid = {result_insert.lastrowid}') if result_insert.rowcount == 1 and result_insert.lastrowid > 0: # insert log.info('Insert record') log.debug(result_insert.lastrowid) @@ -106,10 +106,77 @@ def sql_insert(sql:str=None, data:dict=None, table_name:str=None, id_random_leng # ### BEGIN ### Core Help CRUD ### sql_update() ### -def sql_update(sql=None, data=None, table_name=None, rm_id_random=None, id_random_length=None): - log.setLevel(logging.ERROR) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL +def sql_update(sql:str=None, data:dict=None, table_name:str=None, record_id:int=None, record_id_random:str=None, rm_id_random=None, id_random_length:int=8): + log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) + if sql: + sql_update = text(sql) + elif table_name and data: + #if rm_id_random: + #data = lookup_id_random_pop(obj_data=data) + if not data.get('id_random', None) and id_random_length: + data['id_random'] = secrets.token_urlsafe(id_random_length) + + log.debug(data) + + fields_string = [] + for key, value in data.items(): + if key != 'id': # Creating a special exception for the id field. + fields_string.append('`'+str(key) + '` = :' + str(key)) + + sql_set = ', '.join(fields_string) + + if record_id: + log.info('Update record with ID') + data['id'] = record_id + sql = 'UPDATE `'+table_name+'` SET '+ sql_set + ' WHERE id = :id' + elif record_id_random: + log.info('Update record with ID random') + data['id_random'] = record_id_random + sql = 'UPDATE `'+table_name+'` SET '+ sql_set + ' WHERE id_random = :id_random' + elif 'id' in data: + log.info('Update record with ID') + sql = 'UPDATE `'+table_name+'` SET '+ sql_set + ' WHERE id = :id' + elif 'id_random' in data: + # NOTE: For now it is not possible to update the id_random when supplying the id_random as the primary key for a record. + # NOTE: In the future I can use record_id_random=True as a special case SQL UPDATE. + log.info('Update record with ID random') + sql = 'UPDATE `'+table_name+'` SET '+ sql_set + ' WHERE id_random = :id_random' + else: + return False + sql_update = text(sql) + + trans = db.begin() + try: + result_update = db.execute(sql_update, data) + trans.commit() + except Exception as e: + trans.rollback() + log.exception('*** An exception happened. ***') + log.exception(repr(e)) + log.exception('***') + log.exception(str(e)) + log.exception('^^^ exception ^^^') + return False + else: + log.debug(result_update) + 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 + log.info('Update record (with no change???)') # With SQL UPDATE this record may have actually changed + return True + elif result_update.rowcount == 2 and result_update.lastrowid > 0: # update with change + log.warning('Should we be here???') + log.info('Update record with changes') + record_id = result_update.lastrowid + return record_id + else: + log.debug(result_update) + log.debug(vars(result_update)) + log.debug(dir(result_update)) + log.debug(result_update.rowcount) # returns 1 on insert and 2 on update with change + log.debug(result_update.lastrowid) # returns last row ID on insert and update with a change and returns 0 if nothing changed + return False return False # ### END ### Core Help CRUD ### sql_update() ### @@ -118,7 +185,7 @@ def sql_update(sql=None, data=None, table_name=None, rm_id_random=None, id_rando # The catch all SQL INSERT or UPDATE function - STI 2021-02-17 # This one does it all for SQL INSERT and UPDATE queries def sql_insert_or_update(sql:str=None, data:dict=None, table_name:str=None, rm_id_random:bool=None, id_random_length:int=None): - log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) #if sql: pass @@ -165,7 +232,7 @@ def sql_insert_or_update(sql:str=None, data:dict=None, table_name:str=None, rm_i trans = db.begin() try: - result_insert = db.execute(sql_insert_or_update, data) + result_insert_or_update = db.execute(sql_insert_or_update, data) trans.commit() except Exception as e: trans.rollback() @@ -176,23 +243,25 @@ def sql_insert_or_update(sql:str=None, data:dict=None, table_name:str=None, rm_i log.exception('^^^ exception ^^^') return False else: - if result_insert.rowcount == 1 and result_insert.lastrowid > 0: # insert + log.debug(result_insert_or_update) + log.debug(f'rowcount = {result_insert_or_update.rowcount}; lastrowid = {result_insert_or_update.lastrowid}') + if result_insert_or_update.rowcount == 1 and result_insert_or_update.lastrowid > 0: # insert log.info('Insert record') - record_id = result_insert.lastrowid + record_id = result_insert_or_update.lastrowid return record_id - elif result_insert.rowcount == 1 and result_insert.lastrowid == 0: # update with no change + elif result_insert_or_update.rowcount == 1 and result_insert_or_update.lastrowid == 0: # update with no change log.info('Update record with no change') return True - elif result_insert.rowcount == 2 and result_insert.lastrowid > 0: # update with change + elif result_insert_or_update.rowcount == 2 and result_insert_or_update.lastrowid > 0: # update with change log.info('Update record with changes') - record_id = result_insert.lastrowid + record_id = result_insert_or_update.lastrowid return record_id else: - log.debug(result_insert) - log.debug(vars(result_insert)) - log.debug(dir(result_insert)) - log.debug(result_insert.rowcount) # returns 1 on insert and 2 on update with change - log.debug(result_insert.lastrowid) # returns last row ID on insert and update with a change and returns 0 if nothing changed + log.debug(result_insert_or_update) + log.debug(vars(result_insert_or_update)) + log.debug(dir(result_insert_or_update)) + log.debug(result_insert_or_update.rowcount) # returns 1 on insert and 2 on update with change + log.debug(result_insert_or_update.lastrowid) # returns last row ID on insert and update with a change and returns 0 if nothing changed return False return False # ### END ### Core Help CRUD ### sql_insert_or_update() ### @@ -407,7 +476,7 @@ def sql_select(table_name=None, record_id=None, record_id_random=None, field_nam # The catch all SQL DELETE function - STI 2021-02-17 # This one does it all for SQL DELETE queries def sql_delete(table_name:str=None, record_id:int=None, record_id_random:str=None, field_name:str=None, field_value=None, sql:str=None, data:dict=None): - log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) if table_name and (record_id or record_id_random) and not (field_name or field_value or sql or data): diff --git a/app/main.py b/app/main.py index e0402f8..fcfb74f 100644 --- a/app/main.py +++ b/app/main.py @@ -18,7 +18,7 @@ from .lib_general import * from .log import * # Import the routers here first: -from .routers import api_crud, address, websockets # , items, journals, users +from .routers import api_crud, address, contact, websockets # , items, journals, users from .db_sql import db @@ -61,6 +61,14 @@ app.include_router( #dependencies=[Depends(get_account_header)], #responses={404: {'description': 'Not found'}}, ) +app.include_router( + contact.router, + prefix='/contact', + tags=['Contact'], + #dependencies=[Depends(get_token_header)], + #dependencies=[Depends(get_account_header)], + #responses={404: {'description': 'Not found'}}, +) app.include_router( websockets.router, #prefix='/websocket', diff --git a/app/models/contact_model.py b/app/models/contact_model.py index b6b6d08..31b28e1 100644 --- a/app/models/contact_model.py +++ b/app/models/contact_model.py @@ -33,7 +33,7 @@ class Contact_Base(BaseModel): address_id: Optional[int] for_type: Optional[str] - for_id_random: Optional[str] + for_id_random: Optional[str] = None for_id: Optional[int] name: Optional[str] diff --git a/app/routers/address.py b/app/routers/address.py index 086a3b3..4177d3f 100644 --- a/app/routers/address.py +++ b/app/routers/address.py @@ -9,6 +9,8 @@ from ..log import * from app.config import settings from app.db_sql import * +from .api_crud import patch_obj_template + from ..models.address_model import Address_Base from ..models.response_model import * @@ -52,3 +54,66 @@ async def post_address_obj(address: Address_Base, resp_data = base_name(**sql_select_result).dict(by_alias=by_alias, exclude_unset=exclude_unset) return mk_resp(data=resp_data) + + +@router.patch('/{address_id}') +async def patch_address_obj( + address_id: str, + address: Address_Base, + x_account_id: str = Header(...), + return_obj: Optional[bool] = True, + by_alias: Optional[bool] = True, + exclude_unset: Optional[bool] = True, + ): + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(locals()) + + table_name_update = 'address' + obj_id = address_id # actually id_random + obj_data_dict = address.dict(by_alias=False, exclude_unset=True) + obj_data_dict['id'] = redis_lookup_id_random(record_id_random=obj_id, table_name=table_name_update) + obj_data_dict['id_random'] = obj_id + result = patch_obj_template(table_name_update=table_name_update, + data=obj_data_dict, + obj_id=obj_id, + return_obj=True, + by_alias=True, + exclude_unset=True + ) + + return result + + + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(locals()) + + table_name_update = 'address' + table_name_select = 'v_address' + obj_id = address_id + obj_data_dict = address.dict(by_alias=False, exclude_unset=True) + obj_data_dict['id'] = redis_lookup_id_random(record_id_random=obj_id, table_name=table_name_update) + obj_data_dict['id_random'] = address_id # NOTE: Adding this in so the id_random is NOT updated + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(obj_data_dict) + obj_data = lookup_id_random_pop(obj_data_dict) + base_name = Address_Base + + if sql_update_result := sql_update(table_name=table_name_update, data=obj_data): + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(sql_update_result) + #obj_id = sql_update_result + obj_id = obj_data_dict['id'] + else: + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(sql_update_result) + return mk_resp(data=False, status_code=400) + + if sql_select_result := sql_select(table_name=table_name_select, record_id=obj_id): + log.debug(sql_select_result) + + resp_data = base_name(**sql_select_result).dict(by_alias=by_alias, exclude_unset=exclude_unset) + + return mk_resp(data=resp_data) + else: + log.debug(sql_select_result) + return mk_resp(data=False, status_code=404) \ No newline at end of file diff --git a/app/routers/api_crud.py b/app/routers/api_crud.py index 27d061a..c0aaa97 100644 --- a/app/routers/api_crud.py +++ b/app/routers/api_crud.py @@ -208,6 +208,20 @@ async def get_obj(obj_type_l1: str=None, by_alias: Optional[bool] = True, exclude_unset: Optional[bool] = True, ): + """ + Simple select object type with an ID: + - **obj_type_l1, obj_type_l2, obj_type_l3**: + - Examples: + - /account = account + - /user = user + - /user/role = user_role + - /event = event + - /event/exhibit = event_exhibit + - /order = order + - /order/cart = order_cart + - /order/cart/line = order_cart_line + - /lu/some_lookup = lu_some_lookup + """ log.setLevel(logging.WARNING) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL log.debug(locals()) @@ -250,10 +264,123 @@ async def get_obj(obj_type_l1: str=None, table_name = obj_type_li[obj_name]['table_name'] # NOTE: Add a check for the object ID... assuming it is a random ID string for now. - sql_result = sql_select(table_name=table_name, record_id_random=obj_id) + 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'] + resp_data = base_name(**sql_result).dict(by_alias=by_alias, exclude_unset=exclude_unset) + + return mk_resp(data=resp_data) #, details=debug_data) + else: + log.debug(sql_result) + return mk_resp(data=False, status_code=404) + + +@router.delete('/{obj_type_l1}/{obj_id}') +@router.delete('/{obj_type_l1}/{obj_type_l2}/{obj_id}') +@router.delete('/{obj_type_l1}/{obj_type_l2}/{obj_type_l3}/{obj_id}') +async def delete_obj(obj_type_l1: str=None, + obj_type_l2: str=None, + obj_type_l3: str=None, + obj_id: str=None, + x_account_id: str = Header(...), + by_alias: Optional[bool] = True, + exclude_unset: Optional[bool] = True, + ): + """ + Simple delete object type with an ID: + - **obj_type_l1, obj_type_l2, obj_type_l3**: + - Examples: + - /account = account + - /user = user + - /user/role = user_role + - /event = event + - /event/exhibit = event_exhibit + - /order = order + - /order/cart = order_cart + - /order/cart/line = order_cart_line + - /lu/some_lookup = lu_some_lookup + """ + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(locals()) + + debug_data = {} + debug_data['obj_type_l1'] = obj_type_l1 + debug_data['obj_type_l2'] = obj_type_l2 + debug_data['obj_type_l3'] = obj_type_l3 + debug_data['obj_id'] = obj_id + + log.debug(debug_data) + + if obj_type_l1 and obj_type_l2 and obj_type_l3: + obj_name = f'{obj_type_l1}_{obj_type_l2}_{obj_type_l3}' + if obj_name in obj_type_li: + pass + else: + return mk_resp(data=False, status_code=400) + elif obj_type_l1 and obj_type_l2: + obj_name = f'{obj_type_l1}_{obj_type_l2}' + if obj_name in obj_type_li: + pass + else: + return mk_resp(data=False, status_code=400) + elif obj_type_l1: + obj_name = f'{obj_type_l1}' + if obj_name in obj_type_li: + pass + else: + return mk_resp(data=False, status_code=400) + else: + log.warning('We should not be here') + return mk_resp(data=False, status_code=400) + + table_name = obj_name # obj_type_li[obj_name]['table_name'] # NOTE: Don't try to use the view! + + # NOTE: Add a check for the object ID... assuming it is a random ID string for now. + sql_result = sql_delete(table_name=table_name, record_id_random=obj_id) log.debug(sql_result) - base_name = obj_type_li[obj_name]['base_name'] - resp_data = base_name(**sql_result).dict(by_alias=by_alias, exclude_unset=exclude_unset) + resp_data = True return mk_resp(data=resp_data) #, details=debug_data) + + +def patch_obj_template(table_name_update:str=None, + data:dict=None, + obj_id:str=None, + return_obj:bool=True, + by_alias:bool=True, + exclude_unset:bool=True + ): + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(locals()) + + obj_data_dict = data + + #obj_data_dict['id'] = redis_lookup_id_random(record_id_random=obj_id, table_name=table_name_update) + obj_data_dict['id_random'] = obj_id # NOTE: Adding this in so the id_random is NOT updated + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(obj_data_dict) + obj_data = lookup_id_random_pop(obj_data_dict) + table_name_select = obj_type_li[table_name_update]['table_name'] + base_name = obj_type_li[table_name_update]['base_name'] + + if sql_update_result := sql_update(table_name=table_name_update, data=obj_data): + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(sql_update_result) + #obj_id = sql_update_result + obj_id = obj_data_dict['id'] + else: + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(sql_update_result) + return mk_resp(data=False, status_code=400) + + if sql_select_result := sql_select(table_name=table_name_select, record_id=obj_id): + log.debug(sql_select_result) + + resp_data = base_name(**sql_select_result).dict(by_alias=by_alias, exclude_unset=exclude_unset) + + return mk_resp(data=resp_data) + else: + log.debug(sql_select_result) + return mk_resp(data=False, status_code=404) \ No newline at end of file diff --git a/app/routers/contact.py b/app/routers/contact.py new file mode 100644 index 0000000..9ca0971 --- /dev/null +++ b/app/routers/contact.py @@ -0,0 +1,112 @@ +import datetime +#from datetime import datetime, time, timedelta +from fastapi import APIRouter, Body, Depends, Header, HTTPException, Query, status +from pydantic import BaseModel, EmailStr, Field +from typing import Dict, List, Optional, Set, Union + +from ..lib_general import * +from ..log import * +from app.config import settings +from app.db_sql import * + +from ..models.contact_model import Contact_Base +from ..models.response_model import * + + +router = APIRouter() + + +# Working on the contact API CRUD - STI 2021-03-08 + + +@router.post('') +async def post_contact_obj(contact: Contact_Base, + x_account_id: str = Header(...), + return_obj: Optional[bool] = True, + by_alias: Optional[bool] = True, + exclude_unset: Optional[bool] = True, + ): + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(locals()) + + table_name_insert = 'contact' + obj_data_dict = contact.dict(by_alias=False, exclude_unset=True) + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(obj_data_dict) + obj_data = lookup_id_random_pop(obj_data_dict) + base_name = Contact_Base + + if sql_insert_result := sql_insert(table_name=table_name_insert, data=obj_data): + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(sql_insert_result) + obj_id = sql_insert_result + else: + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(sql_insert_result) + return mk_resp(data=False, status_code=400) + + table_name_select = 'v_contact' + sql_select_result = sql_select(table_name=table_name_select, record_id=obj_id) + log.debug(sql_select_result) + + resp_data = base_name(**sql_select_result).dict(by_alias=by_alias, exclude_unset=exclude_unset) + + return mk_resp(data=resp_data) + + +@router.patch('/{contact_id}') +async def patch_contact_obj(contact_id: str, + contact: Contact_Base, + x_account_id: str = Header(...), + return_obj: Optional[bool] = True, + by_alias: Optional[bool] = True, + exclude_unset: Optional[bool] = True, + ): + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(locals()) + + table_name_update = 'contact' + obj_id = contact_id # actually id_random + obj_data_dict = contact.dict(by_alias=False, exclude_unset=True) + obj_data_dict['id'] = redis_lookup_id_random(record_id_random=obj_id, table_name=table_name_update) + obj_data_dict['id_random'] = obj_id + result = patch_obj_template(table_name_update=table_name_update, + data=obj_data_dict, + obj_id=obj_id, + return_obj=True, + by_alias=True, + exclude_unset=True + ) + + return result + + table_name_update = 'contact' + table_name_select = 'v_contact' + obj_id = contact_id + obj_data_dict = contact.dict(by_alias=False, exclude_unset=True) + obj_data_dict['id'] = redis_lookup_id_random(record_id_random=obj_id, table_name=table_name_update) + obj_data_dict['id_random'] = contact_id # NOTE: Adding this in so the id_random is NOT updated + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(obj_data_dict) + obj_data = lookup_id_random_pop(obj_data_dict) + base_name = Contact_Base + + if sql_update_result := sql_update(table_name=table_name_update, data=obj_data): + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(sql_update_result) + #obj_id = sql_update_result + obj_id = obj_data_dict['id'] + else: + log.setLevel(logging.DEBUG) # DEBUG, INFO, WARNING, ERROR, EXCEPTION, CRITICAL + log.debug(sql_update_result) + return mk_resp(data=False, status_code=400) + + if sql_select_result := sql_select(table_name=table_name_select, record_id=obj_id): + log.debug(sql_select_result) + + resp_data = base_name(**sql_select_result).dict(by_alias=by_alias, exclude_unset=exclude_unset) + + return mk_resp(data=resp_data) + else: + log.debug(sql_select_result) + return mk_resp(data=False, status_code=404)