From 61b00b8b228fd5155287245f8887f0e606888d67 Mon Sep 17 00:00:00 2001 From: Max Metz Date: Mon, 9 Sep 2024 11:55:05 +0200 Subject: [PATCH 1/4] auto-converting non-lists to list values. --- src/server/BreCal/validators/validation_error.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/BreCal/validators/validation_error.py b/src/server/BreCal/validators/validation_error.py index 9ea846a..bdc06fd 100644 --- a/src/server/BreCal/validators/validation_error.py +++ b/src/server/BreCal/validators/validation_error.py @@ -25,6 +25,7 @@ def create_validation_error_response(ex:ValidationError, status_code:int=400)->t # the following conversion snipped ensures a dictionary output if isinstance(errors, (str,list)): errors = {"undefined_schema":errors} + errors = {k:v if isinstance(v,list) else [v] for k,v in errors.items()} # hence, errors always has the following type: dict[str, list[str]] From 85973ad8483ed3db9073755354380c638889f61e Mon Sep 17 00:00:00 2001 From: Max Metz Date: Mon, 9 Sep 2024 12:21:32 +0200 Subject: [PATCH 2/4] creating a default error response format for each API route --- src/server/BreCal/api/berths.py | 3 ++- src/server/BreCal/api/history.py | 7 +++--- src/server/BreCal/api/notifications.py | 3 ++- src/server/BreCal/api/participant.py | 3 ++- src/server/BreCal/api/shipcalls.py | 8 +++---- src/server/BreCal/api/ships.py | 12 +++++----- src/server/BreCal/api/times.py | 10 ++++---- src/server/BreCal/api/user.py | 5 ++-- .../BreCal/validators/validation_error.py | 23 +++++++++++++++---- 9 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/server/BreCal/api/berths.py b/src/server/BreCal/api/berths.py index ba8f4ff..57d960e 100644 --- a/src/server/BreCal/api/berths.py +++ b/src/server/BreCal/api/berths.py @@ -3,6 +3,7 @@ from webargs.flaskparser import parser from .. import impl from ..services.auth_guard import auth_guard import json +from BreCal.validators.validation_error import create_dynamic_exception_response bp = Blueprint('berths', __name__) @@ -15,4 +16,4 @@ def GetBerths(): token = request.headers.get('Authorization') return impl.berths.GetBerths(token) else: - return json.dumps("not authenticated"), 403 + return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated") diff --git a/src/server/BreCal/api/history.py b/src/server/BreCal/api/history.py index c1fe5ad..1cf99e4 100644 --- a/src/server/BreCal/api/history.py +++ b/src/server/BreCal/api/history.py @@ -2,20 +2,21 @@ from flask import Blueprint, request from .. import impl from ..services.auth_guard import auth_guard import json +from BreCal.validators.validation_error import create_dynamic_exception_response bp = Blueprint('history', __name__) @bp.route('/history', methods=['get']) @auth_guard() # no restriction by role -def GetParticipant(): +def GetHistory(): if 'Authorization' in request.headers: token = request.headers.get('Authorization') options = {} if not 'shipcall_id' in request.args: - return json.dumps("missing parameter"), 400 + return create_dynamic_exception_response(ex=None, status_code=400, message="missing parameter: shipcall_id") options["shipcall_id"] = request.args.get("shipcall_id") return impl.history.GetHistory(options) else: - return json.dumps("not authenticated"), 403 + return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated") diff --git a/src/server/BreCal/api/notifications.py b/src/server/BreCal/api/notifications.py index 48a2a44..b68a75f 100644 --- a/src/server/BreCal/api/notifications.py +++ b/src/server/BreCal/api/notifications.py @@ -3,6 +3,7 @@ from .. import impl from ..services.auth_guard import auth_guard import logging import json +from BreCal.validators.validation_error import create_dynamic_exception_response bp = Blueprint('notifications', __name__) @@ -16,4 +17,4 @@ def GetNotifications(): return impl.notifications.GetNotifications(options) else: logging.warning("attempt to load notifications without shipcall id") - return json.dumps("missing argument"), 400 \ No newline at end of file + return create_dynamic_exception_response(ex=None, status_code=400, message="missing argument: shipcall_id") diff --git a/src/server/BreCal/api/participant.py b/src/server/BreCal/api/participant.py index 65887f9..53a369f 100644 --- a/src/server/BreCal/api/participant.py +++ b/src/server/BreCal/api/participant.py @@ -2,6 +2,7 @@ from flask import Blueprint, request from .. import impl from ..services.auth_guard import auth_guard import json +from BreCal.validators.validation_error import create_dynamic_exception_response bp = Blueprint('participants', __name__) @@ -15,5 +16,5 @@ def GetParticipant(): options["user_id"] = request.args.get("user_id") return impl.participant.GetParticipant(options) else: - return json.dumps("not authenticated"), 403 + return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated") diff --git a/src/server/BreCal/api/shipcalls.py b/src/server/BreCal/api/shipcalls.py index e6ac854..743c85b 100644 --- a/src/server/BreCal/api/shipcalls.py +++ b/src/server/BreCal/api/shipcalls.py @@ -7,7 +7,7 @@ from ..services.auth_guard import auth_guard, check_jwt from BreCal.validators.input_validation import validate_posted_shipcall_data, check_if_user_is_bsmd_type from BreCal.validators.input_validation_shipcall import InputValidationShipcall from BreCal.database.sql_handler import execute_sql_query_standalone -from BreCal.validators.validation_error import create_validation_error_response, create_werkzeug_error_response +from BreCal.validators.validation_error import create_validation_error_response, create_werkzeug_error_response, create_dynamic_exception_response from . import verify_if_request_is_json import logging @@ -37,7 +37,7 @@ def GetShipcalls(): return impl.shipcalls.GetShipcalls(options) else: - return json.dumps("not authenticated"), 403 + return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated") @bp.route('/shipcalls', methods=['post']) @@ -65,7 +65,7 @@ def PostShipcalls(): logging.error(ex) logging.error(traceback.format_exc()) print(ex) - return json.dumps("bad format"), 400 + return create_dynamic_exception_response(ex=ex, status_code=400, message="bad format") return impl.shipcalls.PostShipcalls(loadedModel) @@ -99,6 +99,6 @@ def PutShipcalls(): except Exception as ex: logging.error(ex) print(ex) - return json.dumps("bad format"), 400 + return create_dynamic_exception_response(ex=ex, status_code=400, message="bad format") return impl.shipcalls.PutShipcalls(loadedModel) diff --git a/src/server/BreCal/api/ships.py b/src/server/BreCal/api/ships.py index 9f0c43e..9237502 100644 --- a/src/server/BreCal/api/ships.py +++ b/src/server/BreCal/api/ships.py @@ -6,7 +6,7 @@ from ..schemas import model import json import logging from . import verify_if_request_is_json -from BreCal.validators.validation_error import create_validation_error_response +from BreCal.validators.validation_error import create_validation_error_response, create_dynamic_exception_response from BreCal.validators.input_validation import check_if_user_is_bsmd_type from BreCal.validators.input_validation_ship import InputValidationShip @@ -21,7 +21,7 @@ def GetShips(): token = request.headers.get('Authorization') return impl.ships.GetShips(token) else: - return json.dumps("not authenticated"), 403 + return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated") @bp.route('/ships', methods=['post']) @@ -54,7 +54,7 @@ def PostShip(): except Exception as ex: logging.error(ex) print(ex) - return json.dumps(repr(ex)), 400 + return create_dynamic_exception_response(ex=ex, status_code=400, message=None) return impl.ships.PostShip(loadedModel) @@ -83,7 +83,7 @@ def PutShip(): except Exception as ex: logging.error(ex) print(ex) - return json.dumps(repr(ex)), 400 + return create_dynamic_exception_response(ex=ex, status_code=400, message=None) return impl.ships.PutShip(loadedModel) @@ -103,7 +103,7 @@ def DeleteShip(): options = {} options["id"] = request.args.get("id") else: - return json.dumps("no id provided"), 400 + return create_dynamic_exception_response(ex=ex, status_code=400, message="no id provided") # validate the request data & user permissions InputValidationShip.evaluate_delete_data(user_data, ship_id) @@ -116,6 +116,6 @@ def DeleteShip(): except Exception as ex: logging.error(ex) print(ex) - return json.dumps(repr(ex)), 400 + return create_dynamic_exception_response(ex=ex, status_code=400, message=None) return impl.ships.DeleteShip(options) diff --git a/src/server/BreCal/api/times.py b/src/server/BreCal/api/times.py index 65b18ed..5bf3626 100644 --- a/src/server/BreCal/api/times.py +++ b/src/server/BreCal/api/times.py @@ -7,7 +7,7 @@ import logging from marshmallow import ValidationError from BreCal.validators.input_validation_times import InputValidationTimes from . import verify_if_request_is_json -from BreCal.validators.validation_error import create_validation_error_response, create_werkzeug_error_response +from BreCal.validators.validation_error import create_validation_error_response, create_werkzeug_error_response, create_dynamic_exception_response bp = Blueprint('times', __name__) @@ -50,7 +50,7 @@ def PostTimes(): except Exception as ex: logging.error(ex) print(ex) - return json.dumps("bad format"), 400 + return create_dynamic_exception_response(ex=ex, status_code=400, message="bad format") return impl.times.PostTimes(loadedModel) @@ -79,7 +79,7 @@ def PutTimes(): except Exception as ex: logging.error(ex) print(ex) - return json.dumps("bad format"), 400 + return create_dynamic_exception_response(ex=ex, status_code=400, message="bad format") return impl.times.PutTimes(loadedModel) @@ -102,7 +102,7 @@ def DeleteTimes(): return impl.times.DeleteTimes(options) else: logging.warning("Times delete missing id argument") - return json.dumps("missing argument"), 400 + return create_dynamic_exception_response(ex=None, status_code=400, message="missing argument: id") except ValidationError as ex: logging.error(ex) @@ -112,4 +112,4 @@ def DeleteTimes(): except Exception as ex: logging.error(ex) print(ex) - return json.dumps("bad format"), 400 + return create_dynamic_exception_response(ex=ex, status_code=400, message="bad format") diff --git a/src/server/BreCal/api/user.py b/src/server/BreCal/api/user.py index 6416704..86b992b 100644 --- a/src/server/BreCal/api/user.py +++ b/src/server/BreCal/api/user.py @@ -6,6 +6,7 @@ import json import logging from marshmallow import ValidationError from . import verify_if_request_is_json +from BreCal.validators.validation_error import create_dynamic_exception_response, create_validation_error_response bp = Blueprint('user', __name__) @@ -22,12 +23,12 @@ def PutUser(): except ValidationError as ex: logging.error(ex) print(ex) - return json.dumps(f"bad format. \nError Messages: {ex.messages}. \nValid Data: {ex.valid_data}"), 400 + return create_validation_error_response(ex=ex, status_code=400) except Exception as ex: logging.error(ex) print(ex) - return json.dumps("bad format"), 400 + return create_dynamic_exception_response(ex=ex, status_code=400, message="bad format") return impl.user.PutUser(loadedModel) diff --git a/src/server/BreCal/validators/validation_error.py b/src/server/BreCal/validators/validation_error.py index bdc06fd..b9b2059 100644 --- a/src/server/BreCal/validators/validation_error.py +++ b/src/server/BreCal/validators/validation_error.py @@ -6,6 +6,14 @@ import werkzeug from werkzeug.exceptions import Forbidden +def create_default_error_format(error_description): + json_response = { + "message":f"{error_description}", + "errors":[], + "valid_data":{} + } + return json_response + def create_validation_error_response(ex:ValidationError, status_code:int=400)->typing.Tuple[str,int]: # generate an overview the errors @@ -39,11 +47,11 @@ def create_validation_error_response(ex:ValidationError, status_code:int=400)->t valid_data = ex.valid_data - json_response = { - "message":"ValidationError", + json_response = create_default_error_format(error_description="ValidationError") + json_response.update({ "errors":errors, "valid_data":valid_data - } + }) # json.dumps with default=str automatically converts non-serializable values to strings. Hence, datetime objects (which are not) # natively serializable are properly serialized. @@ -53,6 +61,13 @@ def create_validation_error_response(ex:ValidationError, status_code:int=400)->t def create_werkzeug_error_response(ex:Forbidden, status_code:int=403)->typing.Tuple[str,int]: # json.dumps with default=str automatically converts non-serializable values to strings. Hence, datetime objects (which are not) # natively serializable are properly serialized. - serialized_response = json.dumps({"message":ex.description}, default=str) + json_response = create_default_error_format(error_description=ex.description) + serialized_response = json.dumps(json_response, default=str) return serialized_response, status_code +def create_dynamic_exception_response(ex, status_code:int=400, message:typing.Optional[str]=None): + message = repr(ex) if message is None else message + json_response = create_default_error_format(error_description=message) + + serialized_response = json.dumps(json_response, default=str) + return (serialized_response, status_code) From d2cd233f130ca6ebf9bc3460929855462ab91834 Mon Sep 17 00:00:00 2001 From: Max Metz Date: Mon, 9 Sep 2024 12:46:55 +0200 Subject: [PATCH 3/4] creating default handlers for every kind of Exception in the /api/ routes. Those default functions also create automatic logging with the .warning level. Relocated the .impl-calls into the Try-Exception-blocks, so they are properly logged and avoid system failures. --- src/server/BreCal/api/berths.py | 15 ++++-- src/server/BreCal/api/history.py | 22 ++++---- src/server/BreCal/api/notifications.py | 17 +++--- src/server/BreCal/api/participant.py | 18 ++++--- src/server/BreCal/api/shipcalls.py | 52 ++++++++----------- src/server/BreCal/api/ships.py | 38 +++++--------- src/server/BreCal/api/times.py | 32 ++++-------- src/server/BreCal/api/user.py | 8 +-- .../BreCal/validators/validation_error.py | 25 ++++++--- 9 files changed, 113 insertions(+), 114 deletions(-) diff --git a/src/server/BreCal/api/berths.py b/src/server/BreCal/api/berths.py index 57d960e..114f893 100644 --- a/src/server/BreCal/api/berths.py +++ b/src/server/BreCal/api/berths.py @@ -1,3 +1,4 @@ +import logging from flask import Blueprint, request from webargs.flaskparser import parser from .. import impl @@ -12,8 +13,12 @@ bp = Blueprint('berths', __name__) @auth_guard() # no restriction by role def GetBerths(): - if 'Authorization' in request.headers: - token = request.headers.get('Authorization') - return impl.berths.GetBerths(token) - else: - return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated") + try: + if 'Authorization' in request.headers: + token = request.headers.get('Authorization') + return impl.berths.GetBerths(token) + else: + return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated") + + except Exception as ex: + return create_dynamic_exception_response(ex=ex, status_code=400) diff --git a/src/server/BreCal/api/history.py b/src/server/BreCal/api/history.py index 1cf99e4..098423f 100644 --- a/src/server/BreCal/api/history.py +++ b/src/server/BreCal/api/history.py @@ -1,3 +1,4 @@ +import logging from flask import Blueprint, request from .. import impl from ..services.auth_guard import auth_guard @@ -10,13 +11,16 @@ bp = Blueprint('history', __name__) @auth_guard() # no restriction by role def GetHistory(): - if 'Authorization' in request.headers: - token = request.headers.get('Authorization') - options = {} - if not 'shipcall_id' in request.args: - return create_dynamic_exception_response(ex=None, status_code=400, message="missing parameter: shipcall_id") - options["shipcall_id"] = request.args.get("shipcall_id") - return impl.history.GetHistory(options) - else: - return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated") + try: + if 'Authorization' in request.headers: + token = request.headers.get('Authorization') + options = {} + if not 'shipcall_id' in request.args: + return create_dynamic_exception_response(ex=None, status_code=400, message="missing parameter: shipcall_id") + options["shipcall_id"] = request.args.get("shipcall_id") + return impl.history.GetHistory(options) + else: + return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated") + except Exception as ex: + return create_dynamic_exception_response(ex=ex, status_code=400) diff --git a/src/server/BreCal/api/notifications.py b/src/server/BreCal/api/notifications.py index b68a75f..4fe4a8e 100644 --- a/src/server/BreCal/api/notifications.py +++ b/src/server/BreCal/api/notifications.py @@ -11,10 +11,13 @@ bp = Blueprint('notifications', __name__) @bp.route('/notifications', methods=['get']) @auth_guard() # no restriction by role def GetNotifications(): - if 'shipcall_id' in request.args: - options = {} - options["shipcall_id"] = request.args.get("shipcall_id") - return impl.notifications.GetNotifications(options) - else: - logging.warning("attempt to load notifications without shipcall id") - return create_dynamic_exception_response(ex=None, status_code=400, message="missing argument: shipcall_id") + try: + if 'shipcall_id' in request.args: + options = {} + options["shipcall_id"] = request.args.get("shipcall_id") + return impl.notifications.GetNotifications(options) + else: + return create_dynamic_exception_response(ex=None, status_code=400, message="missing argument: shipcall_id") + + except Exception as ex: + return create_dynamic_exception_response(ex=ex, status_code=400) diff --git a/src/server/BreCal/api/participant.py b/src/server/BreCal/api/participant.py index 53a369f..814a3dd 100644 --- a/src/server/BreCal/api/participant.py +++ b/src/server/BreCal/api/participant.py @@ -1,3 +1,4 @@ +import logging from flask import Blueprint, request from .. import impl from ..services.auth_guard import auth_guard @@ -10,11 +11,14 @@ bp = Blueprint('participants', __name__) @auth_guard() # no restriction by role def GetParticipant(): - if 'Authorization' in request.headers: - token = request.headers.get('Authorization') - options = {} - options["user_id"] = request.args.get("user_id") - return impl.participant.GetParticipant(options) - else: - return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated") + try: + if 'Authorization' in request.headers: + token = request.headers.get('Authorization') + options = {} + options["user_id"] = request.args.get("user_id") + return impl.participant.GetParticipant(options) + else: + return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated") + except Exception as ex: + return create_dynamic_exception_response(ex=ex, status_code=400) diff --git a/src/server/BreCal/api/shipcalls.py b/src/server/BreCal/api/shipcalls.py index 743c85b..b21908e 100644 --- a/src/server/BreCal/api/shipcalls.py +++ b/src/server/BreCal/api/shipcalls.py @@ -20,24 +20,28 @@ bp = Blueprint('shipcalls', __name__) @bp.route('/shipcalls', methods=['get']) @auth_guard() # no restriction by role def GetShipcalls(): - if 'Authorization' in request.headers: - token = request.headers.get('Authorization') # see impl/login to see the token encoding, which is a JWT token. + try: + if 'Authorization' in request.headers: + token = request.headers.get('Authorization') # see impl/login to see the token encoding, which is a JWT token. - """ - from BreCal.services.jwt_handler import decode_jwt - jwt = token.split('Bearer ')[1] # string key - payload = decode_jwt(jwt) # dictionary, which includes 'id' (user id) and 'participant_id' + """ + from BreCal.services.jwt_handler import decode_jwt + jwt = token.split('Bearer ')[1] # string key + payload = decode_jwt(jwt) # dictionary, which includes 'id' (user id) and 'participant_id' - # oneline: - payload = decode_jwt(request.headers.get("Authorization").split("Bearer ")[-1]) - """ - options = {} - options["participant_id"] = request.args.get("participant_id") - options["past_days"] = request.args.get("past_days", default=1, type=int) + # oneline: + payload = decode_jwt(request.headers.get("Authorization").split("Bearer ")[-1]) + """ + options = {} + options["participant_id"] = request.args.get("participant_id") + options["past_days"] = request.args.get("past_days", default=1, type=int) - return impl.shipcalls.GetShipcalls(options) - else: - return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated") + return impl.shipcalls.GetShipcalls(options) + else: + return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated") + + except Exception as ex: + return create_dynamic_exception_response(ex=ex, status_code=400) @bp.route('/shipcalls', methods=['post']) @@ -55,20 +59,15 @@ def PostShipcalls(): # validate the posted shipcall data & the user's authority InputValidationShipcall.evaluate_post_data(user_data, loadedModel, content) + return impl.shipcalls.PostShipcalls(loadedModel) except ValidationError as ex: - logging.error(ex) - print(ex) return create_validation_error_response(ex=ex, status_code=400) except Exception as ex: - logging.error(ex) logging.error(traceback.format_exc()) - print(ex) return create_dynamic_exception_response(ex=ex, status_code=400, message="bad format") - return impl.shipcalls.PostShipcalls(loadedModel) - @bp.route('/shipcalls', methods=['put']) @auth_guard() # no restriction by role @@ -85,20 +84,15 @@ def PutShipcalls(): # validate the PUT shipcall data and the user's authority InputValidationShipcall.evaluate_put_data(user_data, loadedModel, content) + return impl.shipcalls.PutShipcalls(loadedModel) except ValidationError as ex: - logging.error(ex) - print(ex) return create_validation_error_response(ex=ex, status_code=400) except werkzeug.exceptions.Forbidden as ex: - logging.error(ex) - print(ex) return create_werkzeug_error_response(ex=ex, status_code=403) except Exception as ex: - logging.error(ex) - print(ex) - return create_dynamic_exception_response(ex=ex, status_code=400, message="bad format") + logging.error(traceback.format_exc()) + return create_dynamic_exception_response(ex=None, status_code=400, message="bad format") - return impl.shipcalls.PutShipcalls(loadedModel) diff --git a/src/server/BreCal/api/ships.py b/src/server/BreCal/api/ships.py index 9237502..9e92ffa 100644 --- a/src/server/BreCal/api/ships.py +++ b/src/server/BreCal/api/ships.py @@ -17,11 +17,15 @@ bp = Blueprint('ships', __name__) @auth_guard() # no restriction by role def GetShips(): - if 'Authorization' in request.headers: - token = request.headers.get('Authorization') - return impl.ships.GetShips(token) - else: - return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated") + try: + if 'Authorization' in request.headers: + token = request.headers.get('Authorization') + return impl.ships.GetShips(token) + else: + return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated") + + except Exception as ex: + return create_dynamic_exception_response(ex=ex, status_code=400) @bp.route('/ships', methods=['post']) @@ -45,19 +49,14 @@ def PostShip(): # validate the request data & user permissions InputValidationShip.evaluate_post_data(user_data, loadedModel, content) + return impl.ships.PostShip(loadedModel) except ValidationError as ex: - logging.error(ex) - print(ex) return create_validation_error_response(ex=ex, status_code=400) except Exception as ex: - logging.error(ex) - print(ex) return create_dynamic_exception_response(ex=ex, status_code=400, message=None) - return impl.ships.PostShip(loadedModel) - @bp.route('/ships', methods=['put']) @auth_guard() # no restriction by role @@ -74,18 +73,13 @@ def PutShip(): # validate the request data & user permissions InputValidationShip.evaluate_put_data(user_data, loadedModel, content) + return impl.ships.PutShip(loadedModel) except ValidationError as ex: - logging.error(ex) - print(ex) return create_validation_error_response(ex=ex, status_code=400) except Exception as ex: - logging.error(ex) - print(ex) - return create_dynamic_exception_response(ex=ex, status_code=400, message=None) - - return impl.ships.PutShip(loadedModel) + return create_dynamic_exception_response(ex=ex, status_code=400) @bp.route('/ships', methods=['delete']) @@ -107,15 +101,11 @@ def DeleteShip(): # validate the request data & user permissions InputValidationShip.evaluate_delete_data(user_data, ship_id) + return impl.ships.DeleteShip(options) except ValidationError as ex: - logging.error(ex) - print(ex) return create_validation_error_response(ex=ex, status_code=400) except Exception as ex: - logging.error(ex) - print(ex) - return create_dynamic_exception_response(ex=ex, status_code=400, message=None) + return create_dynamic_exception_response(ex=ex, status_code=400) - return impl.ships.DeleteShip(options) diff --git a/src/server/BreCal/api/times.py b/src/server/BreCal/api/times.py index 5bf3626..ff8e17e 100644 --- a/src/server/BreCal/api/times.py +++ b/src/server/BreCal/api/times.py @@ -16,10 +16,13 @@ bp = Blueprint('times', __name__) @auth_guard() # no restriction by role def GetTimes(): - options = {} - options["shipcall_id"] = request.args.get("shipcall_id") - - return impl.times.GetTimes(options) + try: + options = {} + options["shipcall_id"] = request.args.get("shipcall_id") + return impl.times.GetTimes(options) + + except Exception as ex: + return create_dynamic_exception_response(ex=ex, status_code=400) @bp.route('/times', methods=['post']) @@ -41,18 +44,14 @@ def PostTimes(): # validate the request InputValidationTimes.evaluate_post_data(user_data, loadedModel, content) - + return impl.times.PostTimes(loadedModel) + except ValidationError as ex: - logging.error(ex) - print(ex) return create_validation_error_response(ex=ex, status_code=400) except Exception as ex: - logging.error(ex) - print(ex) return create_dynamic_exception_response(ex=ex, status_code=400, message="bad format") - return impl.times.PostTimes(loadedModel) @bp.route('/times', methods=['put']) @@ -70,18 +69,14 @@ def PutTimes(): # validate the request InputValidationTimes.evaluate_put_data(user_data, loadedModel, content) + return impl.times.PutTimes(loadedModel) except ValidationError as ex: - logging.error(ex) - print(ex) return create_validation_error_response(ex=ex, status_code=400) except Exception as ex: - logging.error(ex) - print(ex) return create_dynamic_exception_response(ex=ex, status_code=400, message="bad format") - return impl.times.PutTimes(loadedModel) @bp.route('/times', methods=['delete']) @@ -101,15 +96,10 @@ def DeleteTimes(): return impl.times.DeleteTimes(options) else: - logging.warning("Times delete missing id argument") - return create_dynamic_exception_response(ex=None, status_code=400, message="missing argument: id") + return create_dynamic_exception_response(ex=None, status_code=400, message="Times delete missing argument: id") except ValidationError as ex: - logging.error(ex) - print(ex) return create_validation_error_response(ex=ex, status_code=400) except Exception as ex: - logging.error(ex) - print(ex) return create_dynamic_exception_response(ex=ex, status_code=400, message="bad format") diff --git a/src/server/BreCal/api/user.py b/src/server/BreCal/api/user.py index 86b992b..b77c12c 100644 --- a/src/server/BreCal/api/user.py +++ b/src/server/BreCal/api/user.py @@ -19,16 +19,12 @@ def PutUser(): content = request.get_json(force=True) loadedModel = model.UserSchema().load(data=content, many=False, partial=True) + return impl.user.PutUser(loadedModel) except ValidationError as ex: - logging.error(ex) - print(ex) return create_validation_error_response(ex=ex, status_code=400) except Exception as ex: - logging.error(ex) - print(ex) - return create_dynamic_exception_response(ex=ex, status_code=400, message="bad format") + return create_dynamic_exception_response(ex=None, status_code=400, message="bad format") - return impl.user.PutUser(loadedModel) diff --git a/src/server/BreCal/validators/validation_error.py b/src/server/BreCal/validators/validation_error.py index b9b2059..36f5cc8 100644 --- a/src/server/BreCal/validators/validation_error.py +++ b/src/server/BreCal/validators/validation_error.py @@ -15,7 +15,7 @@ def create_default_error_format(error_description): return json_response -def create_validation_error_response(ex:ValidationError, status_code:int=400)->typing.Tuple[str,int]: +def create_validation_error_response(ex:ValidationError, status_code:int=400, create_log:bool=True)->typing.Tuple[str,int]: # generate an overview the errors #example: # {'lock_time': ['The provided value must be in the future. Current Time: 2024-09-02 08:23:32.600791, Value: 2024-09-01 08:20:41.853000']} @@ -46,8 +46,8 @@ def create_validation_error_response(ex:ValidationError, status_code:int=400)->t # } valid_data = ex.valid_data - - json_response = create_default_error_format(error_description="ValidationError") + message = "ValidationError" + json_response = create_default_error_format(error_description=message) json_response.update({ "errors":errors, "valid_data":valid_data @@ -56,18 +56,31 @@ def create_validation_error_response(ex:ValidationError, status_code:int=400)->t # json.dumps with default=str automatically converts non-serializable values to strings. Hence, datetime objects (which are not) # natively serializable are properly serialized. serialized_response = json.dumps(json_response, default=str) + + if create_log: + logging.warning(ex) if ex is not None else logging.warning(message) + print(ex) if ex is not None else print(message) return (serialized_response, status_code) -def create_werkzeug_error_response(ex:Forbidden, status_code:int=403)->typing.Tuple[str,int]: +def create_werkzeug_error_response(ex:Forbidden, status_code:int=403, create_log:bool=True)->typing.Tuple[str,int]: # json.dumps with default=str automatically converts non-serializable values to strings. Hence, datetime objects (which are not) # natively serializable are properly serialized. - json_response = create_default_error_format(error_description=ex.description) + message = ex.description + json_response = create_default_error_format(error_description=message) serialized_response = json.dumps(json_response, default=str) + + if create_log: + logging.warning(ex) if ex is not None else logging.warning(message) + print(ex) if ex is not None else print(message) return serialized_response, status_code -def create_dynamic_exception_response(ex, status_code:int=400, message:typing.Optional[str]=None): +def create_dynamic_exception_response(ex, status_code:int=400, message:typing.Optional[str]=None, create_log:bool=True): message = repr(ex) if message is None else message json_response = create_default_error_format(error_description=message) serialized_response = json.dumps(json_response, default=str) + + if create_log: + logging.warning(ex) if ex is not None else logging.warning(message) + print(ex) if ex is not None else print(message) return (serialized_response, status_code) From 9127cdeac8fc3c64ad857a72dbf4a9f11e22064a Mon Sep 17 00:00:00 2001 From: Max Metz Date: Mon, 9 Sep 2024 17:06:03 +0200 Subject: [PATCH 4/4] BSMD users are now allowed to POST times entry, if they are assigned to the ShipcallParticipantMap or the special BSMD-flag-clause takes place --- src/server/BreCal/validators/input_validation_times.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/server/BreCal/validators/input_validation_times.py b/src/server/BreCal/validators/input_validation_times.py index 7e402fa..e0726fc 100644 --- a/src/server/BreCal/validators/input_validation_times.py +++ b/src/server/BreCal/validators/input_validation_times.py @@ -85,13 +85,10 @@ class InputValidationTimes(): # 2.) datasets may only be created, if the respective participant type did not already create one. InputValidationTimes.check_if_entry_already_exists_for_participant_type(user_data, loadedModel, content) - # 3.) only users who are *not* of type BSMD may post times datasets. - InputValidationTimes.check_user_is_not_bsmd_type(user_data) - - # 4.) Reference checking + # 3.) Reference checking InputValidationTimes.check_dataset_references(content) - # 5.) Value checking + # 4.) Value checking InputValidationTimes.check_dataset_values(user_data, loadedModel, content) return @@ -143,6 +140,7 @@ class InputValidationTimes(): @staticmethod def check_user_is_not_bsmd_type(user_data:dict): """a new dataset may only be created by a user who is *not* belonging to participant group BSMD""" + # this method is deprecated for /times POST requests. As the function may be used elsewhere, it will, for now, not be removed is_bsmd = check_if_user_is_bsmd_type(user_data) if is_bsmd: raise ValidationError({"participant_type":f"current user belongs to BSMD. Cannot post 'times' datasets. Found user data: {user_data}"})