If a PUT or DELETE operation is attempted on a non-existant object, 404 is returned

This commit is contained in:
Daniel Schick 2024-10-21 10:37:35 +02:00
parent 4531eda8f1
commit 0d7861ec36
7 changed files with 90 additions and 44 deletions

View File

@ -23,7 +23,7 @@ bp = Blueprint('shipcalls', __name__)
def GetShipcalls(): def GetShipcalls():
try: try:
if 'Authorization' in request.headers: if 'Authorization' in request.headers:
token = request.headers.get('Authorization') # see impl/login to see the token encoding, which is a JWT token. 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 from BreCal.services.jwt_handler import decode_jwt
@ -34,14 +34,14 @@ def GetShipcalls():
payload = decode_jwt(request.headers.get("Authorization").split("Bearer ")[-1]) payload = decode_jwt(request.headers.get("Authorization").split("Bearer ")[-1])
""" """
payload = decode_jwt(request.headers.get("Authorization").split("Bearer ")[-1]) payload = decode_jwt(request.headers.get("Authorization").split("Bearer ")[-1])
options = {} options = {}
options["past_days"] = request.args.get("past_days", default=1, type=int) options["past_days"] = request.args.get("past_days", default=1, type=int)
options["participant_id"] = payload["participant_id"] options["participant_id"] = payload["participant_id"]
return impl.shipcalls.GetShipcalls(options) return impl.shipcalls.GetShipcalls(options)
else: else:
return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated") return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated")
except Exception as ex: except Exception as ex:
return create_dynamic_exception_response(ex=ex, status_code=400) return create_dynamic_exception_response(ex=ex, status_code=400)
@ -62,10 +62,10 @@ def PostShipcalls():
# validate the posted shipcall data & the user's authority # validate the posted shipcall data & the user's authority
InputValidationShipcall.evaluate_post_data(user_data, loadedModel, content) InputValidationShipcall.evaluate_post_data(user_data, loadedModel, content)
return impl.shipcalls.PostShipcalls(loadedModel) return impl.shipcalls.PostShipcalls(loadedModel)
except ValidationError as ex: except ValidationError as ex:
return create_validation_error_response(ex=ex, status_code=400) return create_validation_error_response(ex=ex, status_code=400)
except Exception as ex: except Exception as ex:
logging.error(traceback.format_exc()) logging.error(traceback.format_exc())
return create_dynamic_exception_response(ex=ex, status_code=400, message="bad format") return create_dynamic_exception_response(ex=ex, status_code=400, message="bad format")
@ -77,23 +77,26 @@ def PutShipcalls():
try: try:
verify_if_request_is_json(request) verify_if_request_is_json(request)
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)
# 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()
if not InputValidationShipcall.exists_shipcall_by_id(loadedModel.get("id")):
return create_dynamic_exception_response(ex=None, status_code=404, message="no shipcall found with the provided id")
# validate the PUT shipcall data and the user's authority # validate the PUT shipcall data and the user's authority
InputValidationShipcall.evaluate_put_data(user_data, loadedModel, content) InputValidationShipcall.evaluate_put_data(user_data, loadedModel, content)
return impl.shipcalls.PutShipcalls(loadedModel) return impl.shipcalls.PutShipcalls(loadedModel)
except ValidationError as ex: except ValidationError as ex:
return create_validation_error_response(ex=ex, status_code=400) return create_validation_error_response(ex=ex, status_code=400)
except werkzeug.exceptions.Forbidden as ex: except werkzeug.exceptions.Forbidden as ex:
return create_werkzeug_error_response(ex=ex, status_code=403) return create_werkzeug_error_response(ex=ex, status_code=403)
except Exception as ex: except Exception as ex:
logging.error(traceback.format_exc()) logging.error(traceback.format_exc())
return create_dynamic_exception_response(ex=None, status_code=400, message="bad format") return create_dynamic_exception_response(ex=None, status_code=400, message="bad format")

View File

@ -23,7 +23,7 @@ def GetShips():
return impl.ships.GetShips(token) return impl.ships.GetShips(token)
else: else:
return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated") return create_dynamic_exception_response(ex=None, status_code=403, message="not authenticated")
except Exception as ex: except Exception as ex:
return create_dynamic_exception_response(ex=ex, status_code=400) return create_dynamic_exception_response(ex=ex, status_code=400)
@ -43,7 +43,7 @@ def PostShip():
is_bsmd = check_if_user_is_bsmd_type(user_data) is_bsmd = check_if_user_is_bsmd_type(user_data)
if not is_bsmd: if not is_bsmd:
raise ValidationError({"participant_type":f"current user does not belong to BSMD. Cannot post shipcalls. Found user data: {user_data}"}) raise ValidationError({"participant_type":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)
@ -53,7 +53,7 @@ def PostShip():
except ValidationError as ex: except ValidationError as ex:
return create_validation_error_response(ex=ex, status_code=400) return create_validation_error_response(ex=ex, status_code=400)
except Exception as ex: except Exception as ex:
return create_dynamic_exception_response(ex=ex, status_code=400, message=None) return create_dynamic_exception_response(ex=ex, status_code=400, message=None)
@ -71,13 +71,16 @@ def PutShip():
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)
if not InputValidationShip.exists_ship_by_dict(model=loadedModel):
return create_dynamic_exception_response(ex=None, status_code=404, message="no ship found with the provided id")
# validate the request data & user permissions # validate the request data & user permissions
InputValidationShip.evaluate_put_data(user_data, loadedModel, content) InputValidationShip.evaluate_put_data(user_data, loadedModel, content)
return impl.ships.PutShip(loadedModel) return impl.ships.PutShip(loadedModel)
except ValidationError as ex: except ValidationError as ex:
return create_validation_error_response(ex=ex, status_code=400) return create_validation_error_response(ex=ex, status_code=400)
except Exception as ex: except Exception as ex:
return create_dynamic_exception_response(ex=ex, status_code=400) return create_dynamic_exception_response(ex=ex, status_code=400)
@ -88,10 +91,10 @@ def DeleteShip():
try: try:
verify_if_request_is_json(request) verify_if_request_is_json(request)
# 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()
if 'id' in request.args: if 'id' in request.args:
options = {} options = {}
options["id"] = request.args.get("id") options["id"] = request.args.get("id")
@ -100,12 +103,16 @@ def DeleteShip():
# validate the request data & user permissions # validate the request data & user permissions
ship_id = request.args.get("id") ship_id = request.args.get("id")
if not InputValidationShip.exists_ship_by_id(id=ship_id):
return create_dynamic_exception_response(ex=None, status_code=404, message="no ship found with the provided id")
InputValidationShip.evaluate_delete_data(user_data, ship_id) InputValidationShip.evaluate_delete_data(user_data, ship_id)
return impl.ships.DeleteShip(options) return impl.ships.DeleteShip(options)
except ValidationError as ex: except ValidationError as ex:
return create_validation_error_response(ex=ex, status_code=400) return create_validation_error_response(ex=ex, status_code=400)
except Exception as ex: except Exception as ex:
return create_dynamic_exception_response(ex=ex, status_code=400) return create_dynamic_exception_response(ex=ex, status_code=400)

View File

@ -20,7 +20,7 @@ def GetTimes():
options = {} options = {}
options["shipcall_id"] = request.args.get("shipcall_id") options["shipcall_id"] = request.args.get("shipcall_id")
return impl.times.GetTimes(options) return impl.times.GetTimes(options)
except Exception as ex: except Exception as ex:
return create_dynamic_exception_response(ex=ex, status_code=400) return create_dynamic_exception_response(ex=ex, status_code=400)
@ -60,13 +60,16 @@ def PutTimes():
try: try:
verify_if_request_is_json(request) verify_if_request_is_json(request)
content = request.get_json(force=True) content = request.get_json(force=True)
loadedModel = model.TimesSchema().load(data=content, many=False, partial=True) loadedModel = model.TimesSchema().load(data=content, many=False, partial=True)
# 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()
if not InputValidationTimes.exists_times_by_id(loadedModel.get("id")):
return create_dynamic_exception_response(ex=None, status_code=404, message="no times found with the provided id")
# validate the request # validate the request
InputValidationTimes.evaluate_put_data(user_data, loadedModel, content) InputValidationTimes.evaluate_put_data(user_data, loadedModel, content)
return impl.times.PutTimes(loadedModel) return impl.times.PutTimes(loadedModel)
@ -91,13 +94,16 @@ def DeleteTimes():
# 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()
if not InputValidationTimes.exists_times_by_id(options["id"]):
return create_dynamic_exception_response(ex=None, status_code=404, message="no times found with the provided id")
# validate the request # validate the request
InputValidationTimes.evaluate_delete_data(user_data, times_id = request.args.get("id")) InputValidationTimes.evaluate_delete_data(user_data, times_id = request.args.get("id"))
return impl.times.DeleteTimes(options) return impl.times.DeleteTimes(options)
else: else:
return create_dynamic_exception_response(ex=None, status_code=400, message="Times delete missing argument: id") return create_dynamic_exception_response(ex=None, status_code=400, message="Times delete missing argument: id")
except ValidationError as ex: except ValidationError as ex:
return create_validation_error_response(ex=ex, status_code=400) return create_validation_error_response(ex=ex, status_code=400)

View File

@ -4,19 +4,27 @@ import datetime
def get_user_data_for_id(user_id:int, expiration_time:int=90): def get_user_data_for_id(user_id:int, expiration_time:int=90):
"""debugging function, which is useful to pull user_data from the database, which may be used to create stub data and unit tests""" """debugging function, which is useful to pull user_data from the database, which may be used to create stub data and unit tests"""
query = "SELECT * FROM user where id = ?id?" query = "SELECT * FROM user where id = ?id?"
pdata = execute_sql_query_standalone(query=query, param={"id":user_id}) pdata = execute_sql_query_standalone(query=query, param={"id":user_id}, command_type="single_or_none")
pdata = pdata[0] if len(pdata)>0 else None
assert pdata is not None, f"could not find user with id {user_id}" assert pdata is not None, f"could not find user with id {user_id}"
user_data = {k:v for k,v in pdata.items() if k in ['id','participant_id','first_name','last_name','user_name','user_phone','user_email']} user_data = {k:v for k,v in pdata.items() if k in ['id','participant_id','first_name','last_name','user_name','user_phone','user_email']}
user_data["exp"] = (datetime.datetime.now()+datetime.timedelta(minutes=expiration_time)).timestamp() user_data["exp"] = (datetime.datetime.now()+datetime.timedelta(minutes=expiration_time)).timestamp()
return user_data return user_data
def get_times_data_for_id(times_id:int): def get_times_data_for_id(times_id:int):
"""helper function to load previous times data from the database""" """helper function to load previous times data from the database"""
query = "SELECT * FROM times where id = ?id?" query = "SELECT * FROM times where id = ?id?"
pdata = execute_sql_query_standalone(query=query, param={"id":times_id}) pdata = execute_sql_query_standalone(query=query, param={"id":times_id}, command_type="single_or_none")
pdata = pdata[0] if len(pdata)>0 else None return pdata
assert pdata is not None, f"could not find times with id {times_id}"
def get_ship_data_for_id(ship_id:int):
"""helper function to load previous ship data from the database"""
query = "SELECT * FROM ship where id = ?id?"
pdata = execute_sql_query_standalone(query=query, param={"id":ship_id}, command_type="single_or_none")
return pdata
def get_shipcall_data_for_id(shipcall_id:int):
"""helper function to load previous shipcall data from the database"""
query = "SELECT * FROM shipcall where id = ?id?"
pdata = execute_sql_query_standalone(query=query, param={"id":shipcall_id}, command_type="single_or_none")
return pdata return pdata

View File

@ -6,8 +6,9 @@ from marshmallow import ValidationError
from string import ascii_letters, digits from string import ascii_letters, digits
from BreCal.schemas.model import Ship, Shipcall, Berth, User, Participant, ShipcallType from BreCal.schemas.model import Ship, Shipcall, Berth, User, Participant, ShipcallType
from BreCal.database.sql_handler import execute_sql_query_standalone from BreCal.database.sql_handler import execute_sql_query_standalone
from BreCal.database.sql_queries import SQLQuery from BreCal.database.sql_queries import SQLQuery
from BreCal.database.sql_utils import get_ship_data_for_id
from BreCal.impl.participant import GetParticipant from BreCal.impl.participant import GetParticipant
from BreCal.impl.ships import GetShips from BreCal.impl.ships import GetShips
from BreCal.impl.berths import GetBerths from BreCal.impl.berths import GetBerths
@ -17,6 +18,7 @@ from BreCal.validators.input_validation_utils import check_if_user_is_bsmd_type,
from BreCal.database.sql_handler import execute_sql_query_standalone from BreCal.database.sql_handler import execute_sql_query_standalone
from BreCal.validators.validation_base_utils import check_if_int_is_valid_flag from BreCal.validators.validation_base_utils import check_if_int_is_valid_flag
from BreCal.validators.validation_base_utils import check_if_string_has_special_characters from BreCal.validators.validation_base_utils import check_if_string_has_special_characters
import werkzeug import werkzeug
class InputValidationShip(): class InputValidationShip():
@ -27,23 +29,34 @@ class InputValidationShip():
Example: Example:
InputValidationShip.evaluate(user_data, loadedModel, content) InputValidationShip.evaluate(user_data, loadedModel, content)
When the data violates one of the rules, a marshmallow.ValidationError is raised, which details the issues. When the data violates one of the rules, a marshmallow.ValidationError is raised, which details the issues.
""" """
def __init__(self) -> None: def __init__(self) -> None:
pass pass
@staticmethod
def exists_ship_by_dict(model:dict):
ship_id = model.get("id")
if(ship_id is not None):
return get_ship_data_for_id(ship_id) is not None
return False
@staticmethod
def exists_ship_by_id(id:int):
return get_ship_data_for_id(id) is not None
@staticmethod @staticmethod
def evaluate_post_data(user_data:dict, loadedModel:dict, content:dict): def evaluate_post_data(user_data:dict, loadedModel:dict, content:dict):
# 1.) Only users of type BSMD are allowed to POST # 1.) Only users of type BSMD are allowed to POST
InputValidationShip.check_user_is_bsmd_type(user_data) InputValidationShip.check_user_is_bsmd_type(user_data)
# 2.) The ship IMOs are used as matching keys. They must be unique in the database. # 2.) The ship IMOs are used as matching keys. They must be unique in the database.
InputValidationShip.check_ship_imo_already_exists(loadedModel) InputValidationShip.check_ship_imo_already_exists(loadedModel)
# 3.) Check for reasonable Values (see BreCal.schemas.model.ShipSchema) # 3.) Check for reasonable Values (see BreCal.schemas.model.ShipSchema)
InputValidationShip.optionally_evaluate_bollard_pull_value(content) InputValidationShip.optionally_evaluate_bollard_pull_value(content)
return return
@staticmethod @staticmethod
def evaluate_put_data(user_data:dict, loadedModel:dict, content:dict): def evaluate_put_data(user_data:dict, loadedModel:dict, content:dict):
# 1.) Only users of type BSMD are allowed to PUT # 1.) Only users of type BSMD are allowed to PUT
@ -58,20 +71,20 @@ class InputValidationShip():
# 4.) Check for reasonable Values (see BreCal.schemas.model.ShipSchema) # 4.) Check for reasonable Values (see BreCal.schemas.model.ShipSchema)
InputValidationShip.optionally_evaluate_bollard_pull_value(content) InputValidationShip.optionally_evaluate_bollard_pull_value(content)
return return
@staticmethod @staticmethod
def evaluate_delete_data(user_data:dict, ship_id:typing.Optional[int]): def evaluate_delete_data(user_data:dict, ship_id:typing.Optional[int]):
if ship_id is None: if ship_id is None:
raise ValidationError({"id":f"The ship id must be provided."}) raise ValidationError({"id":f"The ship id must be provided."})
ship_id = int(ship_id) ship_id = int(ship_id)
# 1.) Only users of type BSMD are allowed to PUT # 1.) Only users of type BSMD are allowed to PUT
InputValidationShip.check_user_is_bsmd_type(user_data) InputValidationShip.check_user_is_bsmd_type(user_data)
# 2.) The dataset entry may not be deleted already # 2.) The dataset entry may not be deleted already
InputValidationShip.check_if_entry_is_already_deleted(ship_id) InputValidationShip.check_if_entry_is_already_deleted(ship_id)
return return
@staticmethod @staticmethod
def optionally_evaluate_bollard_pull_value(content:dict): def optionally_evaluate_bollard_pull_value(content:dict):
bollard_pull = content.get("bollard_pull",None) bollard_pull = content.get("bollard_pull",None)
@ -89,7 +102,7 @@ class InputValidationShip():
is_bsmd = check_if_user_is_bsmd_type(user_data) is_bsmd = check_if_user_is_bsmd_type(user_data)
if not is_bsmd: if not is_bsmd:
raise ValidationError({"participant_type":f"current user does not belong to BSMD. Cannot post, put or delete ships. Found user data: {user_data}"}) raise ValidationError({"participant_type":f"current user does not belong to BSMD. Cannot post, put or delete ships. Found user data: {user_data}"})
@staticmethod @staticmethod
def check_ship_imo_already_exists(loadedModel:dict): def check_ship_imo_already_exists(loadedModel:dict):
# get the ships, convert them to a list of JSON dictionaries # get the ships, convert them to a list of JSON dictionaries
@ -104,28 +117,28 @@ class InputValidationShip():
if imo_already_exists: if imo_already_exists:
raise ValidationError({"imo":f"the provided ship IMO {loadedModel.get('imo')} already exists. A ship may only be added, if there is no other ship with the same IMO number."}) raise ValidationError({"imo":f"the provided ship IMO {loadedModel.get('imo')} already exists. A ship may only be added, if there is no other ship with the same IMO number."})
return return
@staticmethod @staticmethod
def put_content_may_not_contain_imo_number(content:dict): def put_content_may_not_contain_imo_number(content:dict):
# IMO is a required field, so it will never be None outside of tests. If so, there is no violation # IMO is a required field, so it will never be None outside of tests. If so, there is no violation
put_data_ship_imo = content.get("imo",None) put_data_ship_imo = content.get("imo",None)
if put_data_ship_imo is None: if put_data_ship_imo is None:
return return
# grab the ship by its ID and compare, whether the IMO is unchanged # grab the ship by its ID and compare, whether the IMO is unchanged
ship = execute_sql_query_standalone(SQLQuery.get_ship_by_id(), param={"id":content.get("id")}, command_type="single", model=Ship) ship = execute_sql_query_standalone(SQLQuery.get_ship_by_id(), param={"id":content.get("id")}, command_type="single", model=Ship)
if put_data_ship_imo != ship.imo: if put_data_ship_imo != ship.imo:
raise ValidationError({"imo":f"The IMO number field may not be changed since it serves the purpose of a primary (matching) key."}) raise ValidationError({"imo":f"The IMO number field may not be changed since it serves the purpose of a primary (matching) key."})
return return
@staticmethod @staticmethod
def content_contains_ship_id(content:dict): def content_contains_ship_id(content:dict):
put_data_ship_id = content.get('id',None) put_data_ship_id = content.get('id',None)
if put_data_ship_id is None: if put_data_ship_id is None:
raise ValidationError({"id":f"The id field is required."}) raise ValidationError({"id":f"The id field is required."})
return return
@staticmethod @staticmethod
def check_if_entry_is_already_deleted(ship_id:typing.Optional[int]): def check_if_entry_is_already_deleted(ship_id:typing.Optional[int]):
""" """
@ -147,4 +160,4 @@ class InputValidationShip():
return return

View File

@ -11,6 +11,7 @@ from BreCal.impl.ships import GetShips
from BreCal.impl.berths import GetBerths from BreCal.impl.berths import GetBerths
from BreCal.database.enums import ParticipantType, ParticipantFlag from BreCal.database.enums import ParticipantType, ParticipantFlag
from BreCal.database.sql_utils import get_shipcall_data_for_id
from BreCal.validators.input_validation_utils import check_if_user_is_bsmd_type, check_if_ship_id_is_valid, check_if_berth_id_is_valid, check_if_participant_ids_are_valid, check_if_participant_ids_and_types_are_valid, get_shipcall_id_dictionary, get_participant_type_from_user_data from BreCal.validators.input_validation_utils import check_if_user_is_bsmd_type, check_if_ship_id_is_valid, check_if_berth_id_is_valid, check_if_participant_ids_are_valid, check_if_participant_ids_and_types_are_valid, get_shipcall_id_dictionary, get_participant_type_from_user_data
from BreCal.database.sql_handler import get_assigned_participant_of_type from BreCal.database.sql_handler import get_assigned_participant_of_type
from BreCal.database.sql_handler import execute_sql_query_standalone from BreCal.database.sql_handler import execute_sql_query_standalone
@ -98,6 +99,10 @@ class InputValidationShipcall():
InputValidationShipcall.check_shipcall_is_canceled(loadedModel, content) InputValidationShipcall.check_shipcall_is_canceled(loadedModel, content)
return return
@staticmethod
def exists_shipcall_by_id(id:int):
return get_shipcall_data_for_id(id) is not None
@staticmethod @staticmethod
def check_shipcall_values(loadedModel:dict, content:dict, forbidden_keys:list=["evaluation", "evaluation_message"], is_put_data:bool=False): def check_shipcall_values(loadedModel:dict, content:dict, forbidden_keys:list=["evaluation", "evaluation_message"], is_put_data:bool=False):
""" """
@ -127,7 +132,7 @@ class InputValidationShipcall():
if is_put_data: if is_put_data:
# the type of a shipcall may not be changed. It can only be set with the initial POST-request. # the type of a shipcall may not be changed. It can only be set with the initial POST-request.
InputValidationShipcall.check_shipcall_type_is_unchanged(loadedModel) InputValidationShipcall.check_shipcall_type_is_unchanged(loadedModel)
InputValidationShipcall.check_times_are_in_future(loadedModel, content) InputValidationShipcall.check_times_are_in_future(loadedModel, content)
# some arguments must not be provided # some arguments must not be provided
@ -365,7 +370,7 @@ class InputValidationShipcall():
""" """
if (eta is None) and (etd is None): if (eta is None) and (etd is None):
return return
time_in_a_year = time_now.replace(time_now.year + 1) time_in_a_year = time_now.replace(time_now.year + 1)
if type_ is None: if type_ is None:
@ -419,7 +424,7 @@ class InputValidationShipcall():
raise ValidationError({"eta":f"'eta' is more than a year in the future. ETA: {eta}."}) raise ValidationError({"eta":f"'eta' is more than a year in the future. ETA: {eta}."})
if etd > time_in_a_year: if etd > time_in_a_year:
raise ValidationError({"etd":f"'etd' is more than a year in the future. ETD: {etd}."}) raise ValidationError({"etd":f"'etd' is more than a year in the future. ETD: {etd}."})
return return
@staticmethod @staticmethod
@ -442,7 +447,7 @@ class InputValidationShipcall():
if (tidal_window_to is not None) and (tidal_window_from is not None): if (tidal_window_to is not None) and (tidal_window_from is not None):
if tidal_window_to < tidal_window_from: if tidal_window_to < tidal_window_from:
raise ValidationError({"tidal_window_to_or_tidal_window_from":f"'tidal_window_to' must take place after 'tidal_window_from'. Incorrect datetime provided. Found 'tidal_window_to': {tidal_window_to}, 'tidal_window_from': {tidal_window_to}."}) raise ValidationError({"tidal_window_to_or_tidal_window_from":f"'tidal_window_to' must take place after 'tidal_window_from'. Incorrect datetime provided. Found 'tidal_window_to': {tidal_window_to}, 'tidal_window_from': {tidal_window_to}."})
if (tidal_window_to is not None and tidal_window_from is None) or (tidal_window_to is None and tidal_window_from is not None): if (tidal_window_to is not None and tidal_window_from is None) or (tidal_window_to is None and tidal_window_from is not None):
raise ValidationError({"tidal_window_to_or_tidal_window_from":f"'tidal_window_to' and 'tidal_window_from' must both be provided."}) raise ValidationError({"tidal_window_to_or_tidal_window_from":f"'tidal_window_to' and 'tidal_window_from' must both be provided."})

View File

@ -128,6 +128,10 @@ class InputValidationTimes():
InputValidationTimes.check_user_belongs_to_same_group_as_dataset_determines(user_data, loadedModel=None, times_id=times_id) InputValidationTimes.check_user_belongs_to_same_group_as_dataset_determines(user_data, loadedModel=None, times_id=times_id)
return return
@staticmethod
def exists_times_by_id(id:int):
return get_times_data_for_id(id) is not None
@staticmethod @staticmethod
def check_if_entry_is_already_deleted(times_id:int): def check_if_entry_is_already_deleted(times_id:int):
""" """