implementing more input-validation-functions for shipcalls and ships. Beginning to refactor some of the validation functions into more readable Python classes.

This commit is contained in:
Max Metz 2024-04-29 18:50:46 +02:00
parent 78d7fcbd5b
commit 6349e4a73c
5 changed files with 59 additions and 13 deletions

View File

@ -4,7 +4,7 @@ from marshmallow import Schema, fields, ValidationError
from ..schemas import model from ..schemas import model
from .. import impl from .. import impl
from ..services.auth_guard import auth_guard, check_jwt from ..services.auth_guard import auth_guard, check_jwt
from BreCal.validators.input_validation import validate_posted_shipcall_data from BreCal.validators.input_validation import validate_posted_shipcall_data, check_if_user_is_bsmd_type
import logging import logging
import json import json
@ -41,9 +41,6 @@ def PostShipcalls():
try: try:
content = request.get_json(force=True) content = request.get_json(force=True)
loadedModel = model.ShipcallSchema().load(data=content, many=False, partial=True) loadedModel = model.ShipcallSchema().load(data=content, many=False, partial=True)
logging.log(20, loadedModel)
logging.log(20, "dev. above: loaded model, below: content")
logging.log(20, content)
# read the user data from the JWT token (set when login is performed) # read the user data from the JWT token (set when login is performed)
user_data = check_jwt() user_data = check_jwt()
@ -72,6 +69,15 @@ def PutShipcalls():
content = request.get_json(force=True) content = request.get_json(force=True)
logging.info(content) logging.info(content)
loadedModel = model.ShipcallSchema().load(data=content, many=False, partial=True) loadedModel = model.ShipcallSchema().load(data=content, many=False, partial=True)
# read the user data from the JWT token (set when login is performed)
user_data = check_jwt()
# check, whether the user belongs to a participant, which is of type ParticipantType.BSMD
# as ParticipantType is an IntFlag, a user belonging to multiple groups is properly evaluated.
is_bsmd = check_if_user_is_bsmd_type(user_data)
if not is_bsmd:
raise ValidationError(f"current user does not belong to BSMD. Cannot post shipcalls. Found user data: {user_data}")
except ValidationError as ex: except ValidationError as ex:
logging.error(ex) logging.error(ex)

View File

@ -1,11 +1,14 @@
from flask import Blueprint, request from flask import Blueprint, request
from .. import impl from .. import impl
from ..services.auth_guard import auth_guard from ..services.auth_guard import auth_guard, check_jwt
from marshmallow import EXCLUDE from marshmallow import EXCLUDE, ValidationError
from ..schemas import model from ..schemas import model
import json import json
import logging import logging
from BreCal.validators.input_validation import check_if_user_is_bsmd_type
bp = Blueprint('ships', __name__) bp = Blueprint('ships', __name__)
@bp.route('/ships', methods=['get']) @bp.route('/ships', methods=['get'])
@ -24,6 +27,15 @@ def GetShips():
def PostShip(): def PostShip():
try: try:
# read the user data from the JWT token (set when login is performed)
user_data = check_jwt()
# check, whether the user belongs to a participant, which is of type ParticipantType.BSMD
# as ParticipantType is an IntFlag, a user belonging to multiple groups is properly evaluated.
is_bsmd = check_if_user_is_bsmd_type(user_data)
if not is_bsmd:
raise ValidationError(f"current user does not belong to BSMD. Cannot post shipcalls. Found user data: {user_data}")
content = request.get_json(force=True) content = request.get_json(force=True)
loadedModel = model.ShipSchema().load(data=content, many=False, partial=True) loadedModel = model.ShipSchema().load(data=content, many=False, partial=True)
except Exception as ex: except Exception as ex:
@ -39,6 +51,15 @@ def PostShip():
def PutShip(): def PutShip():
try: try:
# read the user data from the JWT token (set when login is performed)
user_data = check_jwt()
# check, whether the user belongs to a participant, which is of type ParticipantType.BSMD
# as ParticipantType is an IntFlag, a user belonging to multiple groups is properly evaluated.
is_bsmd = check_if_user_is_bsmd_type(user_data)
if not is_bsmd:
raise ValidationError(f"current user does not belong to BSMD. Cannot post shipcalls. Found user data: {user_data}")
content = request.get_json(force=True) content = request.get_json(force=True)
loadedModel = model.ShipSchema().load(data=content, many=False, partial=True, unknown=EXCLUDE) loadedModel = model.ShipSchema().load(data=content, many=False, partial=True, unknown=EXCLUDE)
except Exception as ex: except Exception as ex:
@ -53,8 +74,16 @@ def PutShip():
@auth_guard() # no restriction by role @auth_guard() # no restriction by role
def DeleteShip(): def DeleteShip():
# TODO check if I am allowed to delete this thing by deriving the participant from the bearer token
try: try:
# read the user data from the JWT token (set when login is performed)
user_data = check_jwt()
# check, whether the user belongs to a participant, which is of type ParticipantType.BSMD
# as ParticipantType is an IntFlag, a user belonging to multiple groups is properly evaluated.
is_bsmd = check_if_user_is_bsmd_type(user_data)
if not is_bsmd:
raise ValidationError(f"current user does not belong to BSMD. Cannot post shipcalls. Found user data: {user_data}")
if 'id' in request.args: if 'id' in request.args:
options = {} options = {}
options["id"] = request.args.get("id") options["id"] = request.args.get("id")

View File

@ -89,12 +89,15 @@ def create_postman_stub_shipcall():
""" """
this function returns the common stub, which is used to POST data to shipcalls via POSTMAN. However, this function returns the common stub, which is used to POST data to shipcalls via POSTMAN. However,
the stub-function is updated with a dynamic ETA in the future, so the POST-request does not fail. the stub-function is updated with a dynamic ETA in the future, so the POST-request does not fail.
Also provides a stub arrival_berth_id, so the POST-request succeeds.
""" """
shipcall = { shipcall = {
'ship_id': 1, 'ship_id': 1,
'type': 1, 'type': 1,
'eta': (datetime.datetime.now()+datetime.timedelta(hours=3)).isoformat(), 'eta': (datetime.datetime.now()+datetime.timedelta(hours=3)).isoformat(),
'voyage': '43B', 'voyage': '43B',
'arrival_berth_id':142,
'tug_required': False, 'tug_required': False,
'pilot_required': True, 'pilot_required': True,
'flags': 0, 'flags': 0,

View File

@ -26,6 +26,10 @@ def check_if_string_has_special_characters(text:str):
""" """
return bool(set(text).difference(ascii_letters + digits)) return bool(set(text).difference(ascii_letters + digits))
def check_if_int_is_valid_flag(value, enum_object):
# e.g., when an IntFlag has the values 1,2,4; the maximum valid value is 7
max_int = sum([int(val) for val in list(enum_object._value2member_map_.values())])
return 0 < value <= max_int
def get_participant_id_dictionary(): def get_participant_id_dictionary():
# get all participants # get all participants
@ -126,11 +130,6 @@ def validate_posted_shipcall_data(user_data:dict, loadedModel:dict, content:dict
"""this function applies more complex validation functions to data, which is sent to a post-request of shipcalls""" """this function applies more complex validation functions to data, which is sent to a post-request of shipcalls"""
# #TODO_refactor: this function is pretty complex. One may instead build an object, which calls the methods separately. # #TODO_refactor: this function is pretty complex. One may instead build an object, which calls the methods separately.
import logging
logging.log(20, "dev")
logging.log(20, user_data)
logging.log(20, loadedModel)
logging.log(20, content)
##### Section 1: check user_data ##### ##### Section 1: check user_data #####
# check, whether the user belongs to a participant, which is of type ParticipantType.BSMD # check, whether the user belongs to a participant, which is of type ParticipantType.BSMD
# as ParticipantType is an IntFlag, a user belonging to multiple groups is properly evaluated. # as ParticipantType is an IntFlag, a user belonging to multiple groups is properly evaluated.
@ -205,7 +204,16 @@ def validate_posted_shipcall_data(user_data:dict, loadedModel:dict, content:dict
raise ValidationError(f"providing 'arrival_berth_id' & 'departure_berth_id' is mandatory. Missing key!") raise ValidationError(f"providing 'arrival_berth_id' & 'departure_berth_id' is mandatory. Missing key!")
if (not eta >= time_now) or (not etd >= time_now) or (not eta >= etd): if (not eta >= time_now) or (not etd >= time_now) or (not eta >= etd):
raise ValidationError(f"'eta' and 'etd' must be in the future. Incorrect datetime provided.") raise ValidationError(f"'eta' and 'etd' must be in the future. Incorrect datetime provided.")
tidal_window_from = loadedModel.get("tidal_window_from", None)
tidal_window_to = loadedModel.get("tidal_window_to", None)
if tidal_window_to is not None:
if not tidal_window_to >= time_now:
raise ValidationError(f"'tidal_window_to' must be in the future. Incorrect datetime provided.")
if tidal_window_from is not None:
if not tidal_window_from >= time_now:
raise ValidationError(f"'tidal_window_from' must be in the future. Incorrect datetime provided.")
# #TODO: len of participants > 0, if agency # #TODO: len of participants > 0, if agency
# * assigned participant for agency # * assigned participant for agency