Skip to content

Commit

Permalink
adding user add/update/verify functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
PhillipsOwen committed Nov 26, 2024
1 parent 6278221 commit e35155a
Show file tree
Hide file tree
Showing 3 changed files with 239 additions and 8 deletions.
77 changes: 77 additions & 0 deletions src/common/pg_impl.py
Original file line number Diff line number Diff line change
Expand Up @@ -601,3 +601,80 @@ def get_instance_names(self, name: str, project_code: str = None) -> EnumType:

# Return Pandas dataframe
return ret_val

def verify_user(self, email: str, password_hash: str) -> dict:
"""
verifies the user has an account and the password is correct.
if the verification is successful, return a JSON object with pass/fail and user account data
:param email:
:param password_hash:
:return:
"""
# init the return value:
ret_val = None

# prep the email param for the SP
if email is None:
email = 'null'
else:
email = f"'{email}'"

# prep the password param for the SP
if password_hash is None:
password_hash = 'null'
else:
password_hash = f"'{password_hash}'"

# build the query. this will also return the users profile
sql = f"SELECT verify_user(_email := {email}, _password_hash := {password_hash});"

# get the info
ret_val = self.exec_sql('apsviz', sql)

# return the result of the inquiry
return ret_val

def add_user(self, **kwargs) -> dict:
"""
Adds the user and profile and returns a pass/fail dict
if the call is successful, returns a pass/fail dict
:param:
:return:
"""
# init the return value:
ret_val = None

# build the query
sql = f"SELECT public.add_user(_email:={kwargs['email']}, _password_hash:={kwargs['password_hash']}, _role_id:={kwargs['role_id']}, _details:={kwargs['details']});"

# get the info
ret_val = self.exec_sql('apsviz', sql)

# Return Pandas dataframe
return ret_val

def update_user(self, **kwargs) -> dict:
"""
Updates the user profile and returns a pass/fail dict
if the call is successful, returns a pass/fail dict
:param :
:return:
"""
# init the return value:
ret_val = None

# create the SQL query
sql = f"SELECT public.update_user({kwargs['email']}, _password_hash:={kwargs['password_hash']}, _role_id:={kwargs['role_id']}, _details:={kwargs['details']});"

# get the info
ret_val = self.exec_sql('apsviz', sql)

# Return Pandas dataframe
return ret_val
4 changes: 2 additions & 2 deletions src/common/pg_utils_multi.py
Original file line number Diff line number Diff line change
Expand Up @@ -251,7 +251,7 @@ def exec_sql(self, db_name: str, sql_stmt: str):
# insure we have a valid DB connection
success = self.get_db_connection(db_info)

# did we get a connection
# if we get a connection
if success:
# init the cursor
cursor = None
Expand All @@ -274,7 +274,7 @@ def exec_sql(self, db_name: str, sql_stmt: str):
# specify a return code on an empty result
ret_val = -1
else:
# get the one and only record of json
# get the result payload
ret_val = ret_val[0]

except Exception:
Expand Down
166 changes: 160 additions & 6 deletions src/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ async def get_ui_data(grid_type: Union[str, None] = Query(default=None), event_t
limit: Union[int, None] = Query(default=7), use_new_wb: Union[bool, None] = Query(default=False),
use_v3_sp: Union[bool, None] = Query(default=False)) -> json:
"""
Gets the json formatted map UI catalog data.
Gets the JSON formatted map UI catalog data.
<br/>Note: Leave filtering params empty if not desired.
<br/>&nbsp;&nbsp;&nbsp;grid_type: Filter by the name of the ECFLOW grid
<br/>&nbsp;&nbsp;&nbsp;event_type: Filter by the event type
Expand All @@ -94,7 +94,8 @@ async def get_ui_data(grid_type: Union[str, None] = Query(default=None), event_t
try:
logger.debug('Params - grid_type: %s, event_type: %s, instance_name: %s, met_class: %s, storm_name: %s, cycle: %s, advisory_number: %s, '
'run_date: %s, end_date: %s, project_code %s, product_type: %s, limit: %s, ensemble_name: %s', grid_type, event_type,
instance_name, met_class, storm_name, cycle, advisory_number, run_date, end_date, project_code, product_type, limit, ensemble_name)
instance_name, met_class, storm_name, cycle, advisory_number, run_date, end_date, project_code, product_type, limit,
ensemble_name)

# init the kwargs variable
kwargs: dict = {}
Expand Down Expand Up @@ -264,7 +265,7 @@ async def get_ui_data_secure(run_id: Union[str, None] = Query(default=None), gri
product_type: Union[str, None] = Query(default=None), limit: Union[int, None] = Query(default=7),
use_new_wb: Union[bool, None] = Query(default=False), use_v3_sp: Union[bool, None] = Query(default=False), ) -> json:
"""
Gets the json formatted map UI catalog data.
Gets the JSON formatted map UI catalog data.
4460-2024020500-gfsforecast
<br/>Note: Leave filtering params empty if not desired.
<br/>&nbsp;&nbsp;&nbsp;run_id: Filter by the run ID
Expand Down Expand Up @@ -353,7 +354,7 @@ async def get_ui_data_file(file_name: Union[str, None] = Query(default='apsviz.j
product_type: Union[str, None] = Query(default=None), limit: Union[int, None] = Query(default=7),
use_new_wb: Union[bool, None] = Query(default=False), use_v3_sp: Union[bool, None] = Query(default=False), ) -> json:
"""
Returns the json formatted map UI catalog data in a file specified.
Returns the JSON formatted map UI catalog data in a file specified.
<br/>Note: Leave filtering params empty if not desired.
<br/>&nbsp;&nbsp;&nbsp;file_name: The name of the output file (default is apsviz.json)
<br/>&nbsp;&nbsp;&nbsp;grid_type: Filter by the name of the grid
Expand Down Expand Up @@ -644,7 +645,7 @@ async def get_station_data_file(file_name: Union[str, None] = Query(default='sta
async def get_catalog_member_records(run_id: Union[str, None] = Query(default=None), project_code: Union[str, None] = Query(default=None),
filter_event_type: Union[str, None] = Query(default=None), limit: Union[int, None] = Query(default=4)) -> json:
"""
Gets the json formatted catalog member data.
Gets the JSON formatted catalog member data.
<br/>Note: Leave filtering params empty if not desired.
<br/>&nbsp;&nbsp;&nbsp;run_id: Filter by the name of the ECFLOW grid. Leaving this empty will result in getting the latest <limit> records.
<br/>&nbsp;&nbsp;&nbsp;project_code: Filter by the project code.
Expand Down Expand Up @@ -713,7 +714,7 @@ async def get_pulldown_data(grid_type: Union[str, None] = Query(default=None), e
end_date: Union[str, None] = Query(default=None), project_code: Union[str, None] = Query(default=None),
product_type: Union[str, None] = Query(default=None), psc_output: bool = False) -> json:
"""
Gets the json formatted UI pulldown data.
Gets the JSON formatted UI pulldown data.
<br/>Note: Leave filtering params empty if not desired.
<br/>&nbsp;&nbsp;&nbsp;grid_type: Filter by the name of the ECFLOW grid
<br/>&nbsp;&nbsp;&nbsp;event_type: Filter by the event type
Expand Down Expand Up @@ -778,3 +779,156 @@ async def get_pulldown_data(grid_type: Union[str, None] = Query(default=None), e

# return to the caller
return JSONResponse(content=ret_val, status_code=status_code, media_type="application/json")


@APP.get('/verify_user', status_code=200, response_model=None)
async def verify_user(email: Union[str, None] = Query(default=None), password_hash: Union[str, None] = Query(default=None)):
"""
Verifies that the user exists and returns their profile if they do.
<br/>&nbsp;&nbsp;&nbsp;The user's email address
<br/>&nbsp;&nbsp;&nbsp;The user's password (hashed)
"""
# pylint: disable=locally-disabled, unused-argument

# init the returned data and HTML status code
ret_val: dict = {}
status_code: int = 200

try:
# try to make the call for records
ret_val: dict = db_info.verify_user(email, password_hash)

# check the return
if ret_val['success']:
# create the JWT payload
payload = {'bearer_name': os.environ.get("BEARER_NAME"), 'bearer_secret': os.environ.get("BEARER_SECRET")}

# create an access token
token = security.sign_jwt(payload)

# create a new dict element with the JWT token
ret_val['token'] = token['access_token']
# the verification was not successful
else:
ret_val = {'Error': "Could not verify the user's credentials."}

# set the status to a server error
status_code = 404

except Exception:
# return a failure message
ret_val = {'Error': 'Exception detected trying to verify the user.'}

# log the exception
logger.exception(ret_val)

# set the status to a server error
status_code = 500

# return to the caller
return JSONResponse(content=ret_val, status_code=status_code, media_type="application/json")


@APP.get('/update_user', status_code=200, response_model=None)
async def update_user(email: Union[str, None] = Query(default=None), password_hash: Union[str, None] = Query(default=None),
role_id: Union[str, None] = Query(default=None), details: Union[str, None] = Query(default=None)):
"""
update_user the user profile.
<br/>&nbsp;&nbsp;&nbsp;The user's email address
<br/>&nbsp;&nbsp;&nbsp;The user's password (hashed)
<br/>&nbsp;&nbsp;&nbsp;The user's role
<br/>&nbsp;&nbsp;&nbsp;The user's details
"""
# pylint: disable=locally-disabled, unused-argument

# init the returned data and HTML status code
ret_val: dict = {}
status_code: int = 200

try:
# init the kwargs variable
kwargs: dict = {}

# create the param list
params: list = ['email', 'password_hash', 'role_id', 'details']

# loop through the SP params passed in
for param in params:
# add this parm to the list
kwargs.update({param: 'null' if not locals()[param] else f"'{locals()[param]}'"})

# try to make the call for records
ret_val: dict = db_info.update_user(**kwargs)

# check the return
if not ret_val['success']:
ret_val = {'Error': 'Database error updating the users information.'}

# set the status to a server error
status_code = 500

except Exception:
# return a failure message
ret_val = {'Error': 'Exception detected trying to update the user profile.'}

# log the exception
logger.exception(ret_val)

# set the status to a server error
status_code = 500

# return to the caller
return JSONResponse(content=ret_val, status_code=status_code, media_type="application/json")


@APP.get('/add_user', status_code=200, response_model=None)
async def add_user(email: Union[str, None] = Query(default=None), password_hash: Union[str, None] = Query(default=None),
role_id: Union[str, None] = Query(default=None), details: Union[str, None] = Query(default=None)):
"""
Adds the user and their profile.
<br/>&nbsp;&nbsp;&nbsp;The user's email address
<br/>&nbsp;&nbsp;&nbsp;The user's password (hashed)
<br/>&nbsp;&nbsp;&nbsp;The user's role
<br/>&nbsp;&nbsp;&nbsp;The user's details
"""
# pylint: disable=locally-disabled, unused-argument

# init the returned data and HTML status code
ret_val: dict = {}
status_code: int = 200

try: # init the kwargs variable
kwargs: dict = {}

# create the param list
params: list = ['email', 'password_hash', 'role_id', 'details']

# loop through the SP params passed in
for param in params:
# add this parm to the list
kwargs.update({param: 'null' if not locals()[param] else f"'{locals()[param]}'"})

# try to make the call for records
ret_val: dict = db_info.add_user(**kwargs)

# check the return
if not ret_val['success']:
ret_val = {'Error': 'Database error adding the user.'}

# set the status to a server error
status_code = 500

except Exception:
# return a failure message
ret_val = {'Error': 'Exception detected trying to add the user.'}

# log the exception
logger.exception(ret_val)

# set the status to a server error
status_code = 500

# return to the caller
return JSONResponse(content=ret_val, status_code=status_code, media_type="application/json")

0 comments on commit e35155a

Please sign in to comment.